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
840 * - 'and' should not go between a hundreds place value and any
841 * tens/ones place values that follows it. i.e 136 is ein hundert
842 * sechs und dreizig, not ein hundert und sechs und dreizig.
845 ast_copy_string(fn, "digits/minus", sizeof(fn));
846 if ( num > INT_MIN ) {
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));
878 } else if (num == 1000 && t == 0) {
879 ast_copy_string(fn, "digits/thousand", sizeof(fn));
881 } else if (num < 1000000) {
882 int thousands = num / 1000;
885 if (thousands == 1) {
886 ast_copy_string(fn, "digits/1N", sizeof(fn));
887 ast_copy_string(fna, "digits/thousand", sizeof(fna));
889 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
892 ast_copy_string(fn, "digits/thousand", sizeof(fn));
894 } else if (num < 1000000000) {
895 int millions = num / 1000000;
899 ast_copy_string(fn, "digits/1F", sizeof(fn));
900 ast_copy_string(fna, "digits/million", sizeof(fna));
902 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
905 ast_copy_string(fn, "digits/millions", sizeof(fn));
907 } else if (num <= INT_MAX) {
908 int billions = num / 1000000000;
909 num = num % 1000000000;
912 ast_copy_string(fn, "digits/1F", sizeof(fn));
913 ast_copy_string(fna, "digits/milliard", sizeof(fna));
915 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
919 ast_copy_string(fn, "digits/milliards", sizeof(fn));
922 ast_debug(1, "Number '%d' is too big for me\n", num);
926 if (!ast_streamfile(chan, fn, language)) {
927 if ((audiofd > -1) && (ctrlfd > -1))
928 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
930 res = ast_waitstream(chan, ints);
932 ast_stopstream(chan);
934 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
935 if ((audiofd > -1) && (ctrlfd > -1))
936 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
938 res = ast_waitstream(chan, ints);
940 ast_stopstream(chan);
948 /*! \brief ast_say_number_full_en_GB: British syntax
950 - In addition to American English, the following sounds are required: "and"
952 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
959 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
961 while (!res && (num || playh || playa )) {
963 ast_copy_string(fn, "digits/minus", sizeof(fn));
964 if ( num > INT_MIN ) {
970 ast_copy_string(fn, "digits/hundred", sizeof(fn));
973 ast_copy_string(fn, "digits/and", sizeof(fn));
975 } else if (num < 20) {
976 snprintf(fn, sizeof(fn), "digits/%d", num);
978 } else if (num < 100) {
979 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
981 } else if (num < 1000) {
982 int hundreds = num / 100;
983 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
986 num -= 100 * hundreds;
989 } else if (num < 1000000) {
990 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
993 ast_copy_string(fn, "digits/thousand", sizeof(fn));
995 if (num && num < 100)
997 } else if (num < 1000000000) {
998 int millions = num / 1000000;
999 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1002 ast_copy_string(fn, "digits/million", sizeof(fn));
1004 if (num && num < 100)
1007 ast_debug(1, "Number '%d' is too big for me\n", num);
1012 if (!ast_streamfile(chan, fn, language)) {
1013 if ((audiofd > -1) && (ctrlfd > -1))
1014 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1016 res = ast_waitstream(chan, ints);
1018 ast_stopstream(chan);
1024 /*! \brief ast_say_number_full_es: Spanish syntax
1027 Requires a few new audios:
1028 1F.gsm: feminine 'una'
1029 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
1031 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)
1035 int mf = 0; /* +1 = male; -1 = female */
1038 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1041 if (!strncasecmp(options, "f", 1))
1043 else if (!strncasecmp(options, "m", 1))
1047 while (!res && num) {
1049 ast_copy_string(fn, "digits/minus", sizeof(fn));
1050 if ( num > INT_MIN ) {
1056 ast_copy_string(fn, "digits/and", sizeof(fn));
1058 } else if (num == 1) {
1060 snprintf(fn, sizeof(fn), "digits/%dF", num);
1062 snprintf(fn, sizeof(fn), "digits/%dM", num);
1064 snprintf(fn, sizeof(fn), "digits/%d", num);
1066 } else if (num < 31) {
1067 snprintf(fn, sizeof(fn), "digits/%d", num);
1069 } else if (num < 100) {
1070 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1074 } else if (num == 100) {
1075 ast_copy_string(fn, "digits/100", sizeof(fn));
1077 } else if (num < 200) {
1078 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1082 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1084 } else if (num < 2000) {
1086 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1088 if (num < 1000000) {
1089 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1093 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1095 if (num < 2147483640) {
1096 if ((num/1000000) == 1) {
1097 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1100 ast_copy_string(fn, "digits/million", sizeof(fn));
1102 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1105 ast_copy_string(fn, "digits/millions", sizeof(fn));
1109 ast_debug(1, "Number '%d' is too big for me\n", num);
1117 if (!ast_streamfile(chan, fn, language)) {
1118 if ((audiofd > -1) && (ctrlfd > -1))
1119 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1121 res = ast_waitstream(chan, ints);
1123 ast_stopstream(chan);
1131 /*! \brief ast_say_number_full_fr: French syntax
1132 Extra sounds needed:
1135 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)
1140 int mf = 1; /* +1 = male; -1 = female */
1143 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1145 if (options && !strncasecmp(options, "f", 1))
1148 while (!res && (num || playh || playa)) {
1150 ast_copy_string(fn, "digits/minus", sizeof(fn));
1151 if ( num > INT_MIN ) {
1157 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1160 ast_copy_string(fn, "digits/et", sizeof(fn));
1162 } else if (num == 1) {
1164 snprintf(fn, sizeof(fn), "digits/%dF", num);
1166 snprintf(fn, sizeof(fn), "digits/%d", num);
1168 } else if (num < 21) {
1169 snprintf(fn, sizeof(fn), "digits/%d", num);
1171 } else if (num < 70) {
1172 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1173 if ((num % 10) == 1) playa++;
1175 } else if (num < 80) {
1176 ast_copy_string(fn, "digits/60", sizeof(fn));
1177 if ((num % 10) == 1) playa++;
1179 } else if (num < 100) {
1180 ast_copy_string(fn, "digits/80", sizeof(fn));
1182 } else if (num < 200) {
1183 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1185 } else if (num < 1000) {
1186 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1189 } else if (num < 2000) {
1190 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1192 } else if (num < 1000000) {
1193 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1196 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1198 } else if (num < 1000000000) {
1199 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1202 ast_copy_string(fn, "digits/million", sizeof(fn));
1203 num = num % 1000000;
1205 ast_debug(1, "Number '%d' is too big for me\n", num);
1209 if (!ast_streamfile(chan, fn, language)) {
1210 if ((audiofd > -1) && (ctrlfd > -1))
1211 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1213 res = ast_waitstream(chan, ints);
1215 ast_stopstream(chan);
1224 * Check doc/lang/hebrew-digits.txt for information about the various
1225 * recordings required to make this translation work properly */
1226 #define SAY_NUM_BUF_SIZE 256
1227 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)
1230 int state = 0; /* no need to save anything */
1231 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1234 char fn[SAY_NUM_BUF_SIZE] = "";
1236 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1239 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1241 if (options && !strncasecmp(options, "m", 1)) {
1244 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1246 /* Do we have work to do? */
1247 while (!res && (num || (state > 0))) {
1248 /* first type of work: play a second sound. In this loop
1249 * we can only play one sound file at a time. Thus playing
1250 * a second one requires repeating the loop just for the
1251 * second file. The variable 'state' remembers where we were.
1252 * state==0 is the normal mode and it means that we continue
1253 * to check if the number num has yet anything left.
1255 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);
1259 } else if (state == 2) {
1260 if ((num >= 11) && (num < 21)) {
1262 snprintf(fn, sizeof(fn), "digits/ve");
1264 snprintf(fn, sizeof(fn), "digits/uu");
1269 snprintf(fn, sizeof(fn), "digits/ve");
1272 snprintf(fn, sizeof(fn), "digits/uu");
1276 snprintf(fn, sizeof(fn), "digits/ve");
1278 snprintf(fn, sizeof(fn), "digits/uu");
1282 snprintf(fn, sizeof(fn), "digits/ve");
1285 snprintf(fn, sizeof(fn), "digits/ve");
1288 snprintf(fn, sizeof(fn), "digits/ve");
1291 snprintf(fn, sizeof(fn), "digits/ve");
1294 snprintf(fn, sizeof(fn), "digits/uu");
1297 snprintf(fn, sizeof(fn), "digits/ve");
1300 snprintf(fn, sizeof(fn), "digits/ve");
1305 } else if (state == 3) {
1306 snprintf(fn, sizeof(fn), "digits/1k");
1308 } else if (num < 0) {
1309 snprintf(fn, sizeof(fn), "digits/minus");
1311 } else if (num < 20) {
1313 snprintf(fn, sizeof(fn), "digits/%d", num);
1315 snprintf(fn, sizeof(fn), "digits/%dm", num);
1318 } else if ((num < 100) && (num >= 20)) {
1319 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1324 } else if ((num >= 100) && (num < 1000)) {
1326 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1327 num = num - (tmpnum * 100);
1328 if ((num > 0) && (num < 11)) {
1331 } else if ((num >= 1000) && (num < 10000)) {
1332 tmpnum = num / 1000;
1333 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1334 num = num - (tmpnum * 1000);
1335 if ((num > 0) && (num < 11)) {
1338 } else if (num < 20000) {
1339 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1342 } else if (num < 1000000) {
1343 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1347 snprintf(fn, sizeof(fn), "digits/1k");
1349 if ((num > 0) && (num < 11)) {
1352 } else if (num < 2000000) {
1353 snprintf(fn, sizeof(fn), "digits/million");
1354 num = num % 1000000;
1355 if ((num > 0) && (num < 11)) {
1358 } else if (num < 3000000) {
1359 snprintf(fn, sizeof(fn), "digits/twomillion");
1360 num = num - 2000000;
1361 if ((num > 0) && (num < 11)) {
1364 } else if (num < 1000000000) {
1365 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1369 snprintf(fn, sizeof(fn), "digits/million");
1370 num = num % 1000000;
1371 if ((num > 0) && (num < 11)) {
1375 ast_debug(1, "Number '%d' is too big for me\n", num);
1380 if (!ast_streamfile(chan, fn, language)) {
1381 if ((audiofd > -1) && (ctrlfd > -1)) {
1382 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1384 res = ast_waitstream(chan, ints);
1387 ast_stopstream(chan);
1393 /*! \brief ast_say_number_full_hu: Hungarian syntax
1395 Extra sounds needed:
1399 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1405 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1409 like english, except numbers up to 29 are from 2 words.
1410 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1413 while(!res && (num || playh)) {
1415 ast_copy_string(fn, "digits/minus", sizeof(fn));
1416 if ( num > INT_MIN ) {
1422 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1424 } else if (num < 11 || num == 20) {
1425 snprintf(fn, sizeof(fn), "digits/%d", num);
1427 } else if (num < 20) {
1428 ast_copy_string(fn, "digits/10en", sizeof(fn));
1430 } else if (num < 30) {
1431 ast_copy_string(fn, "digits/20on", sizeof(fn));
1433 } else if (num < 100) {
1434 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1438 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1442 if (num < 1000000) { /* 1,000,000 */
1443 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1447 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1449 if (num < 1000000000) { /* 1,000,000,000 */
1450 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1454 ast_copy_string(fn, "digits/million", sizeof(fn));
1456 ast_debug(1, "Number '%d' is too big for me\n", num);
1463 if(!ast_streamfile(chan, fn, language)) {
1464 if ((audiofd > -1) && (ctrlfd > -1))
1465 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1467 res = ast_waitstream(chan, ints);
1469 ast_stopstream(chan);
1475 /*! \brief ast_say_number_full_it: Italian */
1476 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1484 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1489 Like english, numbers up to 20 are a single 'word', and others
1490 compound, but with exceptions.
1491 For example 21 is not twenty-one, but there is a single word in 'it'.
1492 Idem for 28 (ie when a the 2nd part of a compund number
1493 starts with a vowel)
1495 There are exceptions also for hundred, thousand and million.
1496 In english 100 = one hundred, 200 is two hundred.
1497 In italian 100 = cento , like to say hundred (without one),
1498 200 and more are like english.
1500 Same applies for thousand:
1501 1000 is one thousand in en, 2000 is two thousand.
1502 In it we have 1000 = mille , 2000 = 2 mila
1504 For million(s) we use the plural, if more than one
1505 Also, one million is abbreviated in it, like on-million,
1506 or 'un milione', not 'uno milione'.
1507 So the right file is provided.
1510 while (!res && (num || playh)) {
1512 ast_copy_string(fn, "digits/minus", sizeof(fn));
1513 if ( num > INT_MIN ) {
1519 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1521 } else if (num < 20) {
1522 snprintf(fn, sizeof(fn), "digits/%d", num);
1524 } else if (num == 21) {
1525 snprintf(fn, sizeof(fn), "digits/%d", num);
1527 } else if (num == 28) {
1528 snprintf(fn, sizeof(fn), "digits/%d", num);
1530 } else if (num == 31) {
1531 snprintf(fn, sizeof(fn), "digits/%d", num);
1533 } else if (num == 38) {
1534 snprintf(fn, sizeof(fn), "digits/%d", num);
1536 } else if (num == 41) {
1537 snprintf(fn, sizeof(fn), "digits/%d", num);
1539 } else if (num == 48) {
1540 snprintf(fn, sizeof(fn), "digits/%d", num);
1542 } else if (num == 51) {
1543 snprintf(fn, sizeof(fn), "digits/%d", num);
1545 } else if (num == 58) {
1546 snprintf(fn, sizeof(fn), "digits/%d", num);
1548 } else if (num == 61) {
1549 snprintf(fn, sizeof(fn), "digits/%d", num);
1551 } else if (num == 68) {
1552 snprintf(fn, sizeof(fn), "digits/%d", num);
1554 } else if (num == 71) {
1555 snprintf(fn, sizeof(fn), "digits/%d", num);
1557 } else if (num == 78) {
1558 snprintf(fn, sizeof(fn), "digits/%d", num);
1560 } else if (num == 81) {
1561 snprintf(fn, sizeof(fn), "digits/%d", num);
1563 } else if (num == 88) {
1564 snprintf(fn, sizeof(fn), "digits/%d", num);
1566 } else if (num == 91) {
1567 snprintf(fn, sizeof(fn), "digits/%d", num);
1569 } else if (num == 98) {
1570 snprintf(fn, sizeof(fn), "digits/%d", num);
1572 } else if (num < 100) {
1573 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1577 if ((num / 100) > 1) {
1578 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1581 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1585 if (num < 1000000) { /* 1,000,000 */
1587 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1592 if ((tempnum / 1000) < 2)
1593 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1594 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1595 ast_copy_string(fn, "digits/thousands", sizeof(fn));
1597 if (num < 1000000000) { /* 1,000,000,000 */
1598 if ((num / 1000000) > 1)
1599 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1604 if ((tempnum / 1000000) < 2)
1605 ast_copy_string(fn, "digits/million", sizeof(fn));
1607 ast_copy_string(fn, "digits/millions", sizeof(fn));
1609 ast_debug(1, "Number '%d' is too big for me\n", num);
1616 if (!ast_streamfile(chan, fn, language)) {
1617 if ((audiofd > -1) && (ctrlfd > -1))
1618 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1620 res = ast_waitstream(chan, ints);
1622 ast_stopstream(chan);
1628 /*! \brief ast_say_number_full_nl: dutch syntax
1629 * New files: digits/nl-en
1631 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1638 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1639 while (!res && (num || playh )) {
1641 ast_copy_string(fn, "digits/minus", sizeof(fn));
1642 if ( num > INT_MIN ) {
1648 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1650 } else if (num < 20) {
1651 snprintf(fn, sizeof(fn), "digits/%d", num);
1653 } else if (num < 100) {
1656 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1660 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
1662 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1665 } else if (num < 200) {
1666 /* hundred, not one-hundred */
1667 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1669 } else if (num < 1000) {
1670 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1675 /* thousand, not one-thousand */
1677 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1678 } else if (num < 10000) { /* 1,100 to 9,9999 */
1679 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1683 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1685 if (num < 1000000) { /* 1,000,000 */
1686 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1690 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1692 if (num < 1000000000) { /* 1,000,000,000 */
1693 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1697 ast_copy_string(fn, "digits/million", sizeof(fn));
1699 ast_debug(1, "Number '%d' is too big for me\n", num);
1707 if (!ast_streamfile(chan, fn, language)) {
1708 if ((audiofd > -1) && (ctrlfd > -1))
1709 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1711 res = ast_waitstream(chan, ints);
1713 ast_stopstream(chan);
1719 /*! \brief ast_say_number_full_no: Norwegian syntax
1721 * In addition to American English, the following sounds are required: "and", "1N"
1723 * The grammar for Norwegian numbers is the same as for English except
1724 * for the following:
1725 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1726 * "and" before the last two digits, i.e. 2034 is "two thousand and
1727 * thirty-four" and 1000012 is "one million and twelve".
1729 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)
1734 int cn = 1; /* +1 = commune; -1 = neuter */
1738 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1740 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1742 while (!res && (num || playh || playa )) {
1744 ast_copy_string(fn, "digits/minus", sizeof(fn));
1745 if ( num > INT_MIN ) {
1751 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1754 ast_copy_string(fn, "digits/and", sizeof(fn));
1756 } else if (num == 1 && cn == -1) {
1757 ast_copy_string(fn, "digits/1N", sizeof(fn));
1759 } else if (num < 20) {
1760 snprintf(fn, sizeof(fn), "digits/%d", num);
1762 } else if (num < 100) {
1763 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1765 } else if (num < 1000) {
1766 int hundreds = num / 100;
1768 ast_copy_string(fn, "digits/1N", sizeof(fn));
1770 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1773 num -= 100 * hundreds;
1776 } else if (num < 1000000) {
1777 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1780 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1782 if (num && num < 100)
1784 } else if (num < 1000000000) {
1785 int millions = num / 1000000;
1786 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1789 ast_copy_string(fn, "digits/million", sizeof(fn));
1791 if (num && num < 100)
1794 ast_debug(1, "Number '%d' is too big for me\n", num);
1799 if (!ast_streamfile(chan, fn, language)) {
1800 if ((audiofd > -1) && (ctrlfd > -1))
1801 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1803 res = ast_waitstream(chan, ints);
1805 ast_stopstream(chan);
1812 char *separator_dziesiatek;
1816 char *dziesiatki[10];
1821 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1827 return odm->rzedy[rzad - 1][0];
1828 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1829 return odm->rzedy[rzad - 1][1];
1831 return odm->rzedy[rzad - 1][2];
1834 static char* pl_append(char* buffer, char* str)
1836 strcpy(buffer, str);
1837 buffer += strlen(str);
1841 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1843 char file_name[255] = "digits/";
1844 strcat(file_name, fn);
1845 ast_debug(1, "Trying to play: %s\n", file_name);
1846 if (!ast_streamfile(chan, file_name, language)) {
1847 if ((audiofd > -1) && (ctrlfd > -1))
1848 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1850 ast_waitstream(chan, ints);
1852 ast_stopstream(chan);
1855 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1857 /* Initialise variables to allow compilation on Debian-stable, etc */
1867 if (i == 0 && rzad > 0) {
1871 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1875 m1000E6 = i % 1000000000;
1876 i1000E6 = i / 1000000000;
1878 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1880 m1000E3 = m1000E6 % 1000000;
1881 i1000E3 = m1000E6 / 1000000;
1883 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1885 m1000 = m1000E3 % 1000;
1886 i1000 = m1000E3 / 1000;
1888 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1894 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1896 if ( m100 > 0 && m100 <=9 ) {
1898 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1900 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1901 } else if (m100 % 10 == 0) {
1902 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1903 } else if (m100 <= 19 ) {
1904 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1905 } else if (m100 != 0) {
1906 if (odm->separator_dziesiatek[0]==' ') {
1907 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1908 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1912 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1913 b = pl_append(b, odm->separator_dziesiatek);
1914 b = pl_append(b, odm->cyfry2[m100 % 10]);
1915 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1920 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1924 /* ast_say_number_full_pl: Polish syntax
1934 1000000000.2 miliardy
1935 1000000000.5 miliardow
1999 70m siedemdziesieciu
2011 90m dziewiedziesieciu
2013 and combinations of eg.: 20_1, 30m_3m, etc...
2016 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)
2018 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2020 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2022 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2024 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2026 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2028 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2030 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2032 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2034 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2036 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2038 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2040 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2042 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2044 /* Initialise variables to allow compilation on Debian-stable, etc */
2047 static odmiana *odmiana_nieosobowa = NULL;
2048 static odmiana *odmiana_meska = NULL;
2049 static odmiana *odmiana_zenska = NULL;
2051 if (odmiana_nieosobowa == NULL) {
2052 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2054 odmiana_nieosobowa->separator_dziesiatek = " ";
2056 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2057 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2058 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2059 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2060 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2061 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2064 if (odmiana_zenska == NULL) {
2065 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2067 odmiana_zenska->separator_dziesiatek = " ";
2069 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2070 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2071 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2072 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2073 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2074 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2077 if (odmiana_meska == NULL) {
2078 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2080 odmiana_meska->separator_dziesiatek = " ";
2082 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2083 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2084 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2085 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2086 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2087 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2091 if (strncasecmp(options, "f", 1) == 0)
2093 else if (strncasecmp(options, "m", 1) == 0)
2096 o = odmiana_nieosobowa;
2098 o = odmiana_nieosobowa;
2100 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2104 /* ast_say_number_full_pt: Portuguese syntax
2106 * Extra sounds needed:
2107 * For feminin all sound files ends with F
2108 * 100E for 100+ something
2109 * 1000000S for plural
2112 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)
2116 int mf = 1; /* +1 = male; -1 = female */
2120 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2122 if (options && !strncasecmp(options, "f", 1))
2125 while (!res && num ) {
2127 ast_copy_string(fn, "digits/minus", sizeof(fn));
2128 if ( num > INT_MIN ) {
2133 } else if (num < 20) {
2134 if ((num == 1 || num == 2) && (mf < 0))
2135 snprintf(fn, sizeof(fn), "digits/%dF", num);
2137 snprintf(fn, sizeof(fn), "digits/%d", num);
2139 } else if (num < 100) {
2140 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2144 } else if (num < 1000) {
2146 ast_copy_string(fn, "digits/100", sizeof(fn));
2148 ast_copy_string(fn, "digits/100E", sizeof(fn));
2150 if (mf < 0 && num > 199)
2151 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2153 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2158 } else if (num < 1000000) {
2160 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2164 ast_copy_string(fn, "digits/1000", sizeof(fn));
2165 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2168 } else if (num < 1000000000) {
2169 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2173 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2175 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2177 if ((num % 1000000) &&
2179 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2180 /* no hundreds and below */
2181 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2183 num = num % 1000000;
2185 /* number is too big */
2186 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2190 if (!ast_streamfile(chan, fn, language)) {
2191 if ((audiofd > -1) && (ctrlfd > -1))
2192 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2194 res = ast_waitstream(chan, ints);
2196 ast_stopstream(chan);
2198 if (!res && playh) {
2199 res = wait_file(chan, ints, "digits/pt-e", language);
2200 ast_stopstream(chan);
2207 /*! \brief ast_say_number_full_se: Swedish syntax
2212 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)
2217 int cn = 1; /* +1 = commune; -1 = neuter */
2221 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2223 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2225 while (num || playh) {
2227 ast_copy_string(fn, "digits/minus", sizeof(fn));
2228 if ( num > INT_MIN ) {
2234 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2236 } else if (start && num < 200 && num > 99 && cn == -1) {
2237 /* Don't say "en hundra" just say "hundra". */
2238 snprintf(fn, sizeof(fn), "digits/hundred");
2240 } else if (num == 1 && cn == -1) { /* En eller ett? */
2241 ast_copy_string(fn, "digits/1N", sizeof(fn));
2243 } else if (num < 20) {
2244 snprintf(fn, sizeof(fn), "digits/%d", num);
2246 } else if (num < 100) { /* Below hundreds - teens and tens */
2247 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2249 } else if (num < 1000) {
2251 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2254 } else if (num < 1000000) { /* 1,000,000 */
2255 /* Always say "ett hundra tusen", not "en hundra tusen" */
2256 res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2261 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2262 } else if (num < 1000000000) { /* 1,000,000,000 */
2263 /* Always say "en miljon", not "ett miljon" */
2264 res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2269 ast_copy_string(fn, "digits/million", sizeof(fn));
2270 } else { /* Miljarder - Billions */
2271 ast_debug(1, "Number '%d' is too big for me\n", num);
2275 if (!ast_streamfile(chan, fn, language)) {
2276 if ((audiofd > -1) && (ctrlfd > -1)) {
2277 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2279 res = ast_waitstream(chan, ints);
2281 ast_stopstream(chan);
2291 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2292 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2298 int last_length = 0;
2302 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2304 while (!res && (num || playh || playt || playz)) {
2306 ast_copy_string(fn, "digits/minus", sizeof(fn));
2307 if ( num > INT_MIN ) {
2313 snprintf(fn, sizeof(fn), "digits/0");
2317 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2320 snprintf(fn, sizeof(fn), "digits/thousand");
2322 } else if (num < 10) {
2323 snprintf(buf, 10, "%d", num);
2324 if (last_length - strlen(buf) > 1 && last_length != 0) {
2325 last_length = strlen(buf);
2329 snprintf(fn, sizeof(fn), "digits/%d", num);
2331 } else if (num < 100) {
2332 snprintf(buf, 10, "%d", num);
2333 if (last_length - strlen(buf) > 1 && last_length != 0) {
2334 last_length = strlen(buf);
2338 last_length = strlen(buf);
2339 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2343 snprintf(buf, 10, "%d", num);
2344 if (last_length - strlen(buf) > 1 && last_length != 0) {
2345 last_length = strlen(buf);
2349 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2351 snprintf(buf, 10, "%d", num);
2352 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2353 last_length = strlen(buf);
2354 num -= ((num / 100) * 100);
2355 } else if (num < 10000){
2356 snprintf(buf, 10, "%d", num);
2357 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2359 snprintf(buf, 10, "%d", num);
2360 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2361 last_length = strlen(buf);
2362 num -= ((num / 1000) * 1000);
2363 } else if (num < 100000000) { /* 100,000,000 */
2364 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2367 snprintf(buf, 10, "%d", num);
2368 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2369 num -= ((num / 10000) * 10000);
2370 last_length = strlen(buf);
2371 snprintf(fn, sizeof(fn), "digits/wan");
2373 if (num < 1000000000) { /* 1,000,000,000 */
2374 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2377 snprintf(buf, 10, "%d", num);
2378 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2379 last_length = strlen(buf);
2380 num -= ((num / 100000000) * 100000000);
2381 snprintf(fn, sizeof(fn), "digits/yi");
2383 ast_debug(1, "Number '%d' is too big for me\n", num);
2389 if (!ast_streamfile(chan, fn, language)) {
2390 if ((audiofd > -1) && (ctrlfd > -1))
2391 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2393 res = ast_waitstream(chan, ints);
2395 ast_stopstream(chan);
2402 * \brief Counting in Urdu, the national language of Pakistan
2405 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)
2412 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2415 while (!res && (num || playh)) {
2417 snprintf(fn, sizeof(fn), "digits/hundred");
2419 } else if (num < 100) {
2420 snprintf(fn, sizeof(fn), "digits/%d", num);
2422 } else if (num < 1000) {
2423 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2425 num -= ((num / 100) * 100);
2426 } else if (num < 100000) { /* 1,00,000 */
2427 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2431 snprintf(fn, sizeof(fn), "digits/thousand");
2432 } else if (num < 10000000) { /* 1,00,00,000 */
2433 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2437 snprintf(fn, sizeof(fn), "digits/lac");
2438 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2439 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2442 num = num % 10000000;
2443 snprintf(fn, sizeof(fn), "digits/crore");
2445 ast_debug(1, "Number '%d' is too big for me\n", num);
2450 if (!ast_streamfile(chan, fn, language)) {
2451 if ((audiofd > -1) && (ctrlfd > -1)) {
2452 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2454 res = ast_waitstream(chan, ints);
2457 ast_stopstream(chan);
2463 /*! \brief determine last digits for thousands/millions (ru) */
2464 static int get_lastdigits_ru(int num) {
2467 } else if (num < 100) {
2468 return get_lastdigits_ru(num % 10);
2469 } else if (num < 1000) {
2470 return get_lastdigits_ru(num % 100);
2472 return 0; /* number too big */
2476 /*! \brief ast_say_number_full_ru: Russian syntax
2479 n00.gsm (one hundred, two hundred, ...)
2482 thousands-i.gsm (tisyachi)
2483 million-a.gsm (milliona)
2489 where 'n' from 1 to 9
2491 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)
2497 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2499 while (!res && (num)) {
2501 ast_copy_string(fn, "digits/minus", sizeof(fn));
2502 if ( num > INT_MIN ) {
2507 } else if (num < 20) {
2508 if (options && strlen(options) == 1 && num < 3) {
2509 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2511 snprintf(fn, sizeof(fn), "digits/%d", num);
2514 } else if (num < 100) {
2515 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2517 } else if (num < 1000){
2518 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2520 } else if (num < 1000000) { /* 1,000,000 */
2521 lastdigits = get_lastdigits_ru(num / 1000);
2523 if (lastdigits < 3) {
2524 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2526 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2530 if (lastdigits == 1) {
2531 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2532 } else if (lastdigits > 1 && lastdigits < 5) {
2533 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2535 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2538 } else if (num < 1000000000) { /* 1,000,000,000 */
2539 lastdigits = get_lastdigits_ru(num / 1000000);
2541 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2544 if (lastdigits == 1) {
2545 ast_copy_string(fn, "digits/million", sizeof(fn));
2546 } else if (lastdigits > 1 && lastdigits < 5) {
2547 ast_copy_string(fn, "digits/million-a", sizeof(fn));
2549 ast_copy_string(fn, "digits/millions", sizeof(fn));
2553 ast_debug(1, "Number '%d' is too big for me\n", num);
2557 if (!ast_streamfile(chan, fn, language)) {
2558 if ((audiofd > -1) && (ctrlfd > -1))
2559 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2561 res = ast_waitstream(chan, ints);
2563 ast_stopstream(chan);
2569 /*! \brief Thai syntax */
2570 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2576 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2578 while(!res && (num || playh)) {
2580 ast_copy_string(fn, "digits/lop", sizeof(fn));
2581 if ( num > INT_MIN ) {
2587 ast_copy_string(fn, "digits/roi", sizeof(fn));
2589 } else if (num < 100) {
2590 if ((num <= 20) || ((num % 10) == 1)) {
2591 snprintf(fn, sizeof(fn), "digits/%d", num);
2594 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2597 } else if (num < 1000) {
2598 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2601 } else if (num < 10000) { /* 10,000 */
2602 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2606 ast_copy_string(fn, "digits/pan", sizeof(fn));
2607 } else if (num < 100000) { /* 100,000 */
2608 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2612 ast_copy_string(fn, "digits/muan", sizeof(fn));
2613 } else if (num < 1000000) { /* 1,000,000 */
2614 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2618 ast_copy_string(fn, "digits/san", sizeof(fn));
2620 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2624 ast_copy_string(fn, "digits/larn", sizeof(fn));
2627 if(!ast_streamfile(chan, fn, language)) {
2628 if ((audiofd > -1) && (ctrlfd > -1))
2629 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2631 res = ast_waitstream(chan, ints);
2633 ast_stopstream(chan);
2639 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
2640 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2650 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2651 while (!res && (num || playh)) {
2653 ast_copy_string(fn, "digits/minus", sizeof(fn));
2654 if ( num > INT_MIN ) {
2660 snprintf(fn, sizeof(fn), "digits/%da", num);
2664 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2667 ast_copy_string(fn, "digits/odd", sizeof(fn));
2669 } else if (playoh) {
2670 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
2672 } else if (playohz) {
2673 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
2675 } else if (num < 20) {
2676 snprintf(fn, sizeof(fn), "digits/%d", num);
2678 } else if (num < 100) {
2679 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2681 if ((num == 5) || (num == 4) || (num == 1)) playl++;
2684 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2686 if (num && (num < 10)) {
2693 if (num < 1000000) { /* 1,000,000 */
2694 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
2698 snprintf(fn, sizeof(fn), "digits/thousand");
2699 if (num && (num < 10)) {
2701 } else if (num && (num < 100)){
2709 if (num < 1000000000) { /* 1,000,000,000 */
2710 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2714 ast_copy_string(fn, "digits/million", sizeof(fn));
2722 if (!ast_streamfile(chan, fn, language)) {
2723 if ((audiofd > -1) && (ctrlfd > -1))
2724 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2726 res = ast_waitstream(chan, ints);
2728 ast_stopstream(chan);
2734 /*! \brief ast_say_enumeration_full: call language-specific functions
2735 * \note Called from AGI */
2736 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2738 if (!strncasecmp(language, "en", 2)) { /* English syntax */
2739 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2740 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
2741 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
2742 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
2743 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
2744 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
2745 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
2746 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
2747 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
2750 /* Default to english */
2751 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2754 /*! \brief ast_say_enumeration_full_en: English syntax
2755 \note This is the default syntax, if no other syntax defined in this file is used */
2756 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2761 while (!res && num) {