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 */
2222 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2224 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2226 while (num || playh) {
2228 ast_copy_string(fn, "digits/minus", sizeof(fn));
2229 if ( num > INT_MIN ) {
2235 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2237 } else if (start && num < 200 && num > 99 && cn == -1) {
2238 /* Don't say "en hundra" just say "hundra". */
2239 snprintf(fn, sizeof(fn), "digits/hundred");
2241 } else if (num == 1 && cn == -1) { /* En eller ett? */
2242 ast_copy_string(fn, "digits/1N", sizeof(fn));
2244 } else if (num < 20) {
2245 snprintf(fn, sizeof(fn), "digits/%d", num);
2247 } else if (num < 100) { /* Below hundreds - teens and tens */
2248 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2250 } else if (num < 1000) {
2252 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2255 } else if (num < 1000000) { /* 1,000,000 */
2256 /* Always say "ett hundra tusen", not "en hundra tusen" */
2257 res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2262 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2263 } else if (num < 1000000000) { /* 1,000,000,000 */
2264 /* Always say "en miljon", not "ett miljon" */
2265 res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2270 ast_copy_string(fn, "digits/million", sizeof(fn));
2271 } else { /* Miljarder - Billions */
2272 ast_debug(1, "Number '%d' is too big for me\n", num);
2276 if (!ast_streamfile(chan, fn, language)) {
2277 if ((audiofd > -1) && (ctrlfd > -1)) {
2278 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2280 res = ast_waitstream(chan, ints);
2282 ast_stopstream(chan);
2292 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2293 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2299 int last_length = 0;
2303 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2305 while (!res && (num || playh || playt || playz)) {
2307 ast_copy_string(fn, "digits/minus", sizeof(fn));
2308 if ( num > INT_MIN ) {
2314 snprintf(fn, sizeof(fn), "digits/0");
2318 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2321 snprintf(fn, sizeof(fn), "digits/thousand");
2323 } else if (num < 10) {
2324 snprintf(buf, 10, "%d", num);
2325 if (last_length - strlen(buf) > 1 && last_length != 0) {
2326 last_length = strlen(buf);
2330 snprintf(fn, sizeof(fn), "digits/%d", num);
2332 } else if (num < 100) {
2333 snprintf(buf, 10, "%d", num);
2334 if (last_length - strlen(buf) > 1 && last_length != 0) {
2335 last_length = strlen(buf);
2339 last_length = strlen(buf);
2340 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2344 snprintf(buf, 10, "%d", num);
2345 if (last_length - strlen(buf) > 1 && last_length != 0) {
2346 last_length = strlen(buf);
2350 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2352 snprintf(buf, 10, "%d", num);
2353 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2354 last_length = strlen(buf);
2355 num -= ((num / 100) * 100);
2356 } else if (num < 10000){
2357 snprintf(buf, 10, "%d", num);
2358 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2360 snprintf(buf, 10, "%d", num);
2361 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2362 last_length = strlen(buf);
2363 num -= ((num / 1000) * 1000);
2364 } else if (num < 100000000) { /* 100,000,000 */
2365 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2368 snprintf(buf, 10, "%d", num);
2369 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2370 num -= ((num / 10000) * 10000);
2371 last_length = strlen(buf);
2372 snprintf(fn, sizeof(fn), "digits/wan");
2374 if (num < 1000000000) { /* 1,000,000,000 */
2375 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2378 snprintf(buf, 10, "%d", num);
2379 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2380 last_length = strlen(buf);
2381 num -= ((num / 100000000) * 100000000);
2382 snprintf(fn, sizeof(fn), "digits/yi");
2384 ast_debug(1, "Number '%d' is too big for me\n", num);
2390 if (!ast_streamfile(chan, fn, language)) {
2391 if ((audiofd > -1) && (ctrlfd > -1))
2392 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2394 res = ast_waitstream(chan, ints);
2396 ast_stopstream(chan);
2403 * \brief Counting in Urdu, the national language of Pakistan
2406 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)
2413 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2416 while (!res && (num || playh)) {
2418 snprintf(fn, sizeof(fn), "digits/hundred");
2420 } else if (num < 100) {
2421 snprintf(fn, sizeof(fn), "digits/%d", num);
2423 } else if (num < 1000) {
2424 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2426 num -= ((num / 100) * 100);
2427 } else if (num < 100000) { /* 1,00,000 */
2428 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2432 snprintf(fn, sizeof(fn), "digits/thousand");
2433 } else if (num < 10000000) { /* 1,00,00,000 */
2434 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2438 snprintf(fn, sizeof(fn), "digits/lac");
2439 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2440 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2443 num = num % 10000000;
2444 snprintf(fn, sizeof(fn), "digits/crore");
2446 ast_debug(1, "Number '%d' is too big for me\n", num);
2451 if (!ast_streamfile(chan, fn, language)) {
2452 if ((audiofd > -1) && (ctrlfd > -1)) {
2453 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2455 res = ast_waitstream(chan, ints);
2458 ast_stopstream(chan);
2464 /*! \brief determine last digits for thousands/millions (ru) */
2465 static int get_lastdigits_ru(int num) {
2468 } else if (num < 100) {
2469 return get_lastdigits_ru(num % 10);
2470 } else if (num < 1000) {
2471 return get_lastdigits_ru(num % 100);
2473 return 0; /* number too big */
2477 /*! \brief ast_say_number_full_ru: Russian syntax
2480 n00.gsm (one hundred, two hundred, ...)
2483 thousands-i.gsm (tisyachi)
2484 million-a.gsm (milliona)
2490 where 'n' from 1 to 9
2492 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)
2498 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2500 while (!res && (num)) {
2502 ast_copy_string(fn, "digits/minus", sizeof(fn));
2503 if ( num > INT_MIN ) {
2508 } else if (num < 20) {
2509 if (options && strlen(options) == 1 && num < 3) {
2510 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2512 snprintf(fn, sizeof(fn), "digits/%d", num);
2515 } else if (num < 100) {
2516 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2518 } else if (num < 1000){
2519 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2521 } else if (num < 1000000) { /* 1,000,000 */
2522 lastdigits = get_lastdigits_ru(num / 1000);
2524 if (lastdigits < 3) {
2525 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2527 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2531 if (lastdigits == 1) {
2532 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2533 } else if (lastdigits > 1 && lastdigits < 5) {
2534 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2536 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2539 } else if (num < 1000000000) { /* 1,000,000,000 */
2540 lastdigits = get_lastdigits_ru(num / 1000000);
2542 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2545 if (lastdigits == 1) {
2546 ast_copy_string(fn, "digits/million", sizeof(fn));
2547 } else if (lastdigits > 1 && lastdigits < 5) {
2548 ast_copy_string(fn, "digits/million-a", sizeof(fn));
2550 ast_copy_string(fn, "digits/millions", sizeof(fn));
2554 ast_debug(1, "Number '%d' is too big for me\n", num);
2558 if (!ast_streamfile(chan, fn, language)) {
2559 if ((audiofd > -1) && (ctrlfd > -1))
2560 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2562 res = ast_waitstream(chan, ints);
2564 ast_stopstream(chan);
2570 /*! \brief Thai syntax */
2571 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2577 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2579 while(!res && (num || playh)) {
2581 ast_copy_string(fn, "digits/lop", sizeof(fn));
2582 if ( num > INT_MIN ) {
2588 ast_copy_string(fn, "digits/roi", sizeof(fn));
2590 } else if (num < 100) {
2591 if ((num <= 20) || ((num % 10) == 1)) {
2592 snprintf(fn, sizeof(fn), "digits/%d", num);
2595 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2598 } else if (num < 1000) {
2599 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2602 } else if (num < 10000) { /* 10,000 */
2603 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2607 ast_copy_string(fn, "digits/pan", sizeof(fn));
2608 } else if (num < 100000) { /* 100,000 */
2609 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2613 ast_copy_string(fn, "digits/muan", sizeof(fn));
2614 } else if (num < 1000000) { /* 1,000,000 */
2615 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2619 ast_copy_string(fn, "digits/san", sizeof(fn));
2621 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2625 ast_copy_string(fn, "digits/larn", sizeof(fn));
2628 if(!ast_streamfile(chan, fn, language)) {
2629 if ((audiofd > -1) && (ctrlfd > -1))
2630 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2632 res = ast_waitstream(chan, ints);
2634 ast_stopstream(chan);
2640 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
2641 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2651 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2652 while (!res && (num || playh)) {
2654 ast_copy_string(fn, "digits/minus", sizeof(fn));
2655 if ( num > INT_MIN ) {
2661 snprintf(fn, sizeof(fn), "digits/%da", num);
2665 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2668 ast_copy_string(fn, "digits/odd", sizeof(fn));
2670 } else if (playoh) {
2671 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
2673 } else if (playohz) {
2674 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
2676 } else if (num < 20) {
2677 snprintf(fn, sizeof(fn), "digits/%d", num);
2679 } else if (num < 100) {
2680 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2682 if ((num == 5) || (num == 4) || (num == 1)) playl++;
2685 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2687 if (num && (num < 10)) {
2694 if (num < 1000000) { /* 1,000,000 */
2695 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
2699 snprintf(fn, sizeof(fn), "digits/thousand");
2700 if (num && (num < 10)) {
2702 } else if (num && (num < 100)){
2710 if (num < 1000000000) { /* 1,000,000,000 */
2711 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2715 ast_copy_string(fn, "digits/million", sizeof(fn));
2723 if (!ast_streamfile(chan, fn, language)) {
2724 if ((audiofd > -1) && (ctrlfd > -1))
2725 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2727 res = ast_waitstream(chan, ints);
2729 ast_stopstream(chan);
2735 /*! \brief ast_say_enumeration_full: call language-specific functions
2736 * \note Called from AGI */
2737 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2739 if (!strncasecmp(language, "en", 2)) { /* English syntax */
2740 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2741 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
2742 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
2743 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
2744 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
2745 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
2746 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
2747 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
2748 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
2751 /* Default to english */
2752 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2755 /*! \brief ast_say_enumeration_full_en: English syntax
2756 \note This is the default syntax, if no other syntax defined in this file is used */
2757 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2762 while (!res && num) {
2764 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2765 if ( num > INT_MIN ) {
2770 } else if (num < 20) {
2771 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2773 } else if (num < 100) {
2774 int tens = num / 10;
2777 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2779 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2781 } else if (num < 1000) {
2782 int hundreds = num / 100;
2784 if (hundreds > 1 || t == 1) {
2785 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2790 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2792 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
2794 } else if (num < 1000000) {
2795 int thousands = num / 1000;
2797 if (thousands > 1 || t == 1) {
2798 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2803 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2805 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
2808 } else if (num < 1000000000) {
2809 int millions = num / 1000000;
2810 num = num % 1000000;
2812 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2816 ast_copy_string(fn, "digits/million", sizeof(fn));
2818 ast_copy_string(fn, "digits/h-million", sizeof(fn));
2820 } else if (num < INT_MAX) {
2821 int billions = num / 1000000000;
2822 num = num % 1000000000;
2824 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2828 ast_copy_string(fn, "digits/billion", sizeof(fn));
2830 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
2832 } else if (num == INT_MAX) {
2833 ast_copy_string(fn, "digits/h-last", sizeof(fn));
2836 ast_debug(1, "Number '%d' is too big for me\n", num);
2841 if (!ast_streamfile(chan, fn, language)) {
2842 if ((audiofd > -1) && (ctrlfd > -1)) {
2843 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2845 res = ast_waitstream(chan, ints);
2848 ast_stopstream(chan);
2854 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2858 ast_copy_string(fn, "digits/h", sizeof(fn));
2860 if (!ast_streamfile(chan, fn, language)) {
2861 if ((audiofd > -1) && (ctrlfd > -1)) {
2862 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2864 res = ast_waitstream(chan, ints);
2867 ast_stopstream(chan);
2870 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
2873 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2874 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)
2876 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2878 char fn[256] = "", fna[256] = "";
2881 if (options && !strncasecmp(options, "f", 1)) {
2883 } else if (options && !strncasecmp(options, "n", 1)) {
2890 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2892 while (!res && num) {
2894 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2895 if ( num > INT_MIN ) {
2900 } else if (num < 100 && t) {
2901 ast_copy_string(fn, "digits/and", sizeof(fn));
2903 } else if (num < 20) {
2904 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2906 } else if (num < 100) {
2907 int ones = num % 10;
2909 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2912 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2915 } else if (num == 100 && t == 0) {
2916 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2918 } else if (num < 1000) {
2919 int hundreds = num / 100;
2921 if (hundreds == 1) {
2922 ast_copy_string(fn, "digits/1N", sizeof(fn));
2924 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2927 ast_copy_string(fna, "digits/hundred", sizeof(fna));
2929 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2932 } else if (num < 1000000) {
2933 int thousands = num / 1000;
2935 if (thousands == 1) {
2937 ast_copy_string(fn, "digits/1N", sizeof(fn));
2938 ast_copy_string(fna, "digits/thousand", sizeof(fna));
2941 ast_copy_string(fn, "digits/1N", sizeof(fn));
2942 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2944 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2948 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2953 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2955 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2959 } else if (num < 1000000000) {
2960 int millions = num / 1000000;
2961 num = num % 1000000;
2962 if (millions == 1) {
2964 ast_copy_string(fn, "digits/1F", sizeof(fn));
2965 ast_copy_string(fna, "digits/million", sizeof(fna));
2967 ast_copy_string(fn, "digits/1N", sizeof(fn));
2968 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2971 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2976 ast_copy_string(fn, "digits/millions", sizeof(fn));
2978 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2982 } else if (num < INT_MAX) {
2983 int billions = num / 1000000000;
2984 num = num % 1000000000;
2985 if (billions == 1) {
2987 ast_copy_string(fn, "digits/1F", sizeof(fn));
2988 ast_copy_string(fna, "digits/milliard", sizeof(fna));
2990 ast_copy_string(fn, "digits/1N", sizeof(fn));
2991 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2994 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2998 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3000 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3004 } else if (num == INT_MAX) {
3005 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3008 ast_debug(1, "Number '%d' is too big for me\n", num);
3013 if (!ast_streamfile(chan, fn, language)) {
3014 if ((audiofd > -1) && (ctrlfd > -1))
3015 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3017 res = ast_waitstream(chan, ints);
3019 ast_stopstream(chan);
3021 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3022 if ((audiofd > -1) && (ctrlfd > -1)) {
3023 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3025 res = ast_waitstream(chan, ints);
3028 ast_stopstream(chan);
3036 /*! \brief ast_say_enumeration_full_de: German syntax */
3037 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)
3039 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
3041 char fn[256] = "", fna[256] = "";
3044 if (options && !strncasecmp(options, "f", 1)) {
3046 } else if (options && !strncasecmp(options, "n", 1)) {
3053 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3055 while (!res && num) {
3057 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3058 if ( num > INT_MIN ) {
3063 } else if (num < 100 && t) {
3064 ast_copy_string(fn, "digits/and", sizeof(fn));
3066 } else if (num < 20) {
3067 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3069 } else if (num < 100) {
3070 int ones = num % 10;
3072 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3075 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3078 } else if (num == 100 && t == 0) {
3079 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3081 } else if (num < 1000) {
3082 int hundreds = num / 100;
3084 if (hundreds == 1) {
3085 ast_copy_string(fn, "digits/1N", sizeof(fn));
3087 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3090 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3092 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3095 } else if (num < 1000000) {
3096 int thousands = num / 1000;
3098 if (thousands == 1) {
3100 ast_copy_string(fn, "digits/1N", sizeof(fn));
3101 ast_copy_string(fna, "digits/thousand", sizeof(fna));