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"
54 #include "asterisk/test.h"
56 /* Forward declaration */
57 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
60 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
63 char fnbuf[10], asciibuf[20] = "letters/ascii";
68 while (str[num] && !res) {
78 fn = "letters/exclaimation-point";
84 fn = "letters/dollar";
93 fn = "letters/equals";
102 fn = "letters/space";
114 strcpy(fnbuf, "digits/X");
120 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
121 strcpy(fnbuf, "letters/X");
125 if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
126 (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
127 res = ast_streamfile(chan, fn, lang);
129 if ((audiofd > -1) && (ctrlfd > -1))
130 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
132 res = ast_waitstream(chan, ints);
134 ast_stopstream(chan);
142 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
150 while (str[num] && !res) {
160 fn = "letters/exclaimation-point";
166 fn = "letters/dollar";
175 fn = "letters/equals";
181 fn = "letters/slash";
184 fn = "letters/space";
195 strcpy(fnbuf, "digits/X");
199 default: /* '9' falls here... */
201 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
202 strcpy(fnbuf, "phonetic/X_p");
206 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
207 res = ast_streamfile(chan, fn, lang);
209 if ((audiofd > -1) && (ctrlfd > -1))
210 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
212 res = ast_waitstream(chan, ints);
214 ast_stopstream(chan);
222 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
229 while (str[num] && !res) {
251 strcpy(fnbuf, "digits/X");
256 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
257 res = ast_streamfile(chan, fn, lang);
259 if ((audiofd > -1) && (ctrlfd > -1))
260 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
262 res = ast_waitstream(chan, ints);
264 ast_stopstream(chan);
272 /* Forward declarations */
273 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
274 \note Not really language codes.
275 For these language codes, Asterisk will change the syntax when
276 saying numbers (and in some cases dates and voicemail messages
280 \arg \b en - English (US)
281 \arg \b en_GB - English (British)
282 \arg \b es - Spanish, Mexican
287 \arg \b no - Norwegian
289 \arg \b pt - Portuguese
290 \arg \b pt_BR - Portuguese (Brazil)
292 \arg \b zh - Taiwanese / Chinese
294 \arg \b ka - Georgian
295 \arg \b hu - Hungarian
298 For Some languages the numbers differ for gender and plural.
299 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
300 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
301 use the option argument 'p' for plural enumerations like in German
303 Date/Time functions currently have less languages supported than saynumber().
305 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
307 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
310 Portuguese sound files needed for Time/Date functions:
321 Spanish sound files needed for Time/Date functions:
326 Italian sound files needed for Time/Date functions:
332 /* Forward declarations of language specific variants of ast_say_number_full */
333 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
334 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
335 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
336 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
337 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
338 static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
339 static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
340 static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
341 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
342 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
343 static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
344 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
345 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
346 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
347 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
348 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
349 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
350 static int ast_say_number_full_ka(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
351 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
352 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
353 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
354 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
356 /* Forward declarations of language specific variants of ast_say_enumeration_full */
357 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
358 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);
359 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);
360 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);
361 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
363 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
364 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
371 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
372 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
373 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
374 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
376 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);
377 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);
378 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);
379 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);
380 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);
381 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);
382 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);
383 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);
384 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);
385 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);
386 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);
387 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);
388 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);
389 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);
391 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
392 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
399 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
404 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
406 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
407 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
408 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
409 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
410 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
411 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
412 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
413 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
414 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
415 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
417 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
418 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
419 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
420 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
421 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
423 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
426 if ((res = ast_streamfile(chan, file, lang))) {
427 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
430 res = ast_waitstream(chan, ints);
435 /*! \brief ast_say_number_full: call language-specific functions
436 \note Called from AGI */
437 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
439 ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
440 if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
441 return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
442 } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
443 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
444 } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
445 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
446 } else if (!strncasecmp(language, "cz", 2)) { /* deprecated Czech syntax */
447 static int deprecation_warning = 0;
448 if (deprecation_warning++ % 10 == 0) {
449 ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
451 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
452 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
453 return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
454 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
455 return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
456 } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
457 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
458 } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
459 return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
460 } else if (!strncasecmp(language, "ge", 2)) { /* deprecated Georgian syntax */
461 static int deprecation_warning = 0;
462 if (deprecation_warning++ % 10 == 0) {
463 ast_log(LOG_WARNING, "ge is not a standard language code. Please switch to using ka instead.\n");
465 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
466 } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
467 return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
468 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
469 return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
470 } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
471 return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
472 } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
473 return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
474 } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
475 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
476 } else if (!strncasecmp(language, "mx", 2)) { /* deprecated Mexican syntax */
477 static int deprecation_warning = 0;
478 if (deprecation_warning++ % 10 == 0) {
479 ast_log(LOG_WARNING, "mx is not a standard language code. Please switch to using es_MX instead.\n");
481 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
482 } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
483 return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
484 } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
485 return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
486 } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
487 return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
488 } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
489 return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
490 } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
491 return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
492 } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
493 return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
494 } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
495 return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
496 } else if (!strncasecmp(language, "tw", 2)) { /* deprecated Taiwanese syntax */
497 static int deprecation_warning = 0;
498 if (deprecation_warning++ % 10 == 0) {
499 ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese. Please switch to using zh_TW instead.\n");
501 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
502 } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
503 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
504 } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
505 return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
506 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
507 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
510 /* Default to english */
511 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
514 /*! \brief ast_say_number_full_en: English syntax
515 \note This is the default syntax, if no other syntax defined in this file is used */
516 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
522 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
524 while (!res && (num || playh)) {
526 ast_copy_string(fn, "digits/minus", sizeof(fn));
527 if ( num > INT_MIN ) {
533 ast_copy_string(fn, "digits/hundred", sizeof(fn));
535 } else if (num < 20) {
536 snprintf(fn, sizeof(fn), "digits/%d", num);
538 } else if (num < 100) {
539 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
543 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
547 if (num < 1000000) { /* 1,000,000 */
548 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
552 snprintf(fn, sizeof(fn), "digits/thousand");
554 if (num < 1000000000) { /* 1,000,000,000 */
555 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
559 ast_copy_string(fn, "digits/million", sizeof(fn));
561 ast_debug(1, "Number '%d' is too big for me\n", num);
568 if (!ast_streamfile(chan, fn, language)) {
569 if ((audiofd > -1) && (ctrlfd > -1))
570 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
572 res = ast_waitstream(chan, ints);
574 ast_stopstream(chan);
580 static int exp10_int(int power)
583 for (x=0;x<power;x++)
588 /*! \brief ast_say_number_full_cs: Czech syntax
591 * - 1m,2m - gender male
592 * - 1w,2w - gender female
596 * - hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
598 * for each number 10^(3n + 3) exist 3 files represented as:
599 * 1 tousand = jeden tisic = 1_E3
600 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
601 * 5,6,... tousands = pet,sest,... tisic = 5_E3
607 * tousand, milion are gender male, so 1 and 2 is 1m 2m
608 * miliard is gender female, so 1 and 2 is 1w 2w
610 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)
620 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
625 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
627 while (!res && (num || playh)) {
629 ast_copy_string(fn, "digits/minus", sizeof(fn));
630 if ( num > INT_MIN ) {
635 } else if (num < 3 ) {
636 snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
639 } else if (num < 20) {
640 snprintf(fn, sizeof(fn), "digits/%d", num);
643 } else if (num < 100) {
644 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
646 } else if (num < 1000) {
647 hundered = num / 100;
648 if ( hundered == 1 ) {
649 ast_copy_string(fn, "digits/1sto", sizeof(fn));
650 } else if ( hundered == 2 ) {
651 ast_copy_string(fn, "digits/2ste", sizeof(fn));
653 res = ast_say_number_full_cs(chan, hundered, ints, language, options, audiofd, ctrlfd);
656 if (hundered == 3 || hundered == 4) {
657 ast_copy_string(fn, "digits/sta", sizeof(fn));
658 } else if ( hundered > 4 ) {
659 ast_copy_string(fn, "digits/set", sizeof(fn));
662 num -= (hundered * 100);
663 } else { /* num > 1000 */
664 length = (int)log10(num)+1;
665 while ( (length % 3 ) != 1 ) {
668 left = num / (exp10_int(length-1));
671 case 9: options = "w"; /* 1,000,000,000 gender female */
673 default : options = "m"; /* others are male */
676 if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
677 res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
681 if ( left >= 5 ) { /* >= 5 have the same declesion */
682 snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
683 } else if ( left >= 2 && left <= 4 ) {
684 snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
685 } else { /* left == 1 */
686 snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
688 num -= left * (exp10_int(length-1));
691 if (!ast_streamfile(chan, fn, language)) {
692 if ((audiofd > -1) && (ctrlfd > -1)) {
693 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
695 res = ast_waitstream(chan, ints);
698 ast_stopstream(chan);
704 /*! \brief ast_say_number_full_da: Danish syntax
706 - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
708 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)
713 int cn = 1; /* +1 = commune; -1 = neuter */
716 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
718 if (options && !strncasecmp(options, "n", 1)) cn = -1;
720 while (!res && (num || playh || playa )) {
721 /* The grammar for Danish numbers is the same as for English except
723 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
724 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
725 * "one-and twenty" and 68 is "eight-and sixty".
726 * - "million" is different in singular and plural form
727 * - numbers > 1000 with zero as the third digit from last have an
728 * "and" before the last two digits, i.e. 2034 is "two thousand and
729 * four-and thirty" and 1000012 is "one million and twelve".
732 ast_copy_string(fn, "digits/minus", sizeof(fn));
733 if ( num > INT_MIN ) {
739 ast_copy_string(fn, "digits/hundred", sizeof(fn));
742 ast_copy_string(fn, "digits/and", sizeof(fn));
744 } else if (num == 1 && cn == -1) {
745 ast_copy_string(fn, "digits/1N", sizeof(fn));
747 } else if (num < 20) {
748 snprintf(fn, sizeof(fn), "digits/%d", num);
750 } else if (num < 100) {
753 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
756 snprintf(fn, sizeof(fn), "digits/%d", num);
761 int hundreds = num / 100;
763 ast_copy_string(fn, "digits/1N", sizeof(fn));
765 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
768 num -= 100 * hundreds;
774 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
778 ast_copy_string(fn, "digits/thousand", sizeof(fn));
780 if (num < 1000000000) {
781 int millions = num / 1000000;
782 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
786 ast_copy_string(fn, "digits/million", sizeof(fn));
788 ast_copy_string(fn, "digits/millions", sizeof(fn));
791 ast_debug(1, "Number '%d' is too big for me\n", num);
795 if (num && num < 100)
800 if (!ast_streamfile(chan, fn, language)) {
801 if ((audiofd > -1) && (ctrlfd > -1))
802 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
804 res = ast_waitstream(chan, ints);
806 ast_stopstream(chan);
812 /*! \brief ast_say_number_full_de: German syntax
815 In addition to English, the following sounds are required:
817 - "1-and" through "9-and"
820 - NB "1" is recorded as 'eins'
822 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)
825 int mf = 1; /* +1 = male and neuter; -1 = female */
829 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
831 if (options && (!strncasecmp(options, "f", 1)))
834 while (!res && num) {
835 /* The grammar for German numbers is the same as for English except
837 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
838 * "one-and twenty" and 68 is "eight-and sixty".
839 * - "one" varies according to gender
840 * - 100 is 'hundert', however all other instances are 'ein hundert'
841 * - 1000 is 'tausend', however all other instances are 'ein tausend'
842 * - 1000000 is always 'eine million'
843 * - "million" is different in singular and plural form
844 * - 'and' should not go between a hundreds place value and any
845 * tens/ones place values that follows it. i.e 136 is ein hundert
846 * sechs und dreizig, not ein hundert und sechs und dreizig.
849 ast_copy_string(fn, "digits/minus", sizeof(fn));
850 if ( num > INT_MIN ) {
855 } else if (num == 1 && mf == -1) {
856 snprintf(fn, sizeof(fn), "digits/%dF", num);
858 } else if (num < 20) {
859 snprintf(fn, sizeof(fn), "digits/%d", num);
861 } else if (num < 100) {
864 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
867 snprintf(fn, sizeof(fn), "digits/%d", num);
870 } else if (num == 100 && t == 0) {
871 ast_copy_string(fn, "digits/hundred", sizeof(fn));
873 } else if (num < 1000) {
874 int hundreds = num / 100;
877 ast_copy_string(fn, "digits/1N", sizeof(fn));
879 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
881 ast_copy_string(fna, "digits/hundred", sizeof(fna));
882 } else if (num == 1000 && t == 0) {
883 ast_copy_string(fn, "digits/thousand", sizeof(fn));
885 } else if (num < 1000000) {
886 int thousands = num / 1000;
889 if (thousands == 1) {
890 ast_copy_string(fn, "digits/1N", sizeof(fn));
891 ast_copy_string(fna, "digits/thousand", sizeof(fna));
893 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
896 ast_copy_string(fn, "digits/thousand", sizeof(fn));
898 } else if (num < 1000000000) {
899 int millions = num / 1000000;
903 ast_copy_string(fn, "digits/1F", sizeof(fn));
904 ast_copy_string(fna, "digits/million", sizeof(fna));
906 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
909 ast_copy_string(fn, "digits/millions", sizeof(fn));
911 } else if (num <= INT_MAX) {
912 int billions = num / 1000000000;
913 num = num % 1000000000;
916 ast_copy_string(fn, "digits/1F", sizeof(fn));
917 ast_copy_string(fna, "digits/milliard", sizeof(fna));
919 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
923 ast_copy_string(fn, "digits/milliards", sizeof(fn));
926 ast_debug(1, "Number '%d' is too big for me\n", num);
930 if (!ast_streamfile(chan, fn, language)) {
931 if ((audiofd > -1) && (ctrlfd > -1))
932 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
934 res = ast_waitstream(chan, ints);
936 ast_stopstream(chan);
938 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
939 if ((audiofd > -1) && (ctrlfd > -1))
940 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
942 res = ast_waitstream(chan, ints);
944 ast_stopstream(chan);
952 /*! \brief ast_say_number_full_en_GB: British syntax
954 - In addition to American English, the following sounds are required: "and"
956 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
963 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
965 while (!res && (num || playh || playa )) {
967 ast_copy_string(fn, "digits/minus", sizeof(fn));
968 if ( num > INT_MIN ) {
974 ast_copy_string(fn, "digits/hundred", sizeof(fn));
977 ast_copy_string(fn, "digits/and", sizeof(fn));
979 } else if (num < 20) {
980 snprintf(fn, sizeof(fn), "digits/%d", num);
982 } else if (num < 100) {
983 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
985 } else if (num < 1000) {
986 int hundreds = num / 100;
987 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
990 num -= 100 * hundreds;
993 } else if (num < 1000000) {
994 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
997 ast_copy_string(fn, "digits/thousand", sizeof(fn));
999 if (num && num < 100)
1001 } else if (num < 1000000000) {
1002 int millions = num / 1000000;
1003 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1006 ast_copy_string(fn, "digits/million", sizeof(fn));
1008 if (num && num < 100)
1011 ast_debug(1, "Number '%d' is too big for me\n", num);
1016 if (!ast_streamfile(chan, fn, language)) {
1017 if ((audiofd > -1) && (ctrlfd > -1))
1018 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1020 res = ast_waitstream(chan, ints);
1022 ast_stopstream(chan);
1028 /*! \brief ast_say_number_full_es: Spanish syntax
1031 Requires a few new audios:
1032 1F.gsm: feminine 'una'
1033 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
1035 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)
1039 int mf = 0; /* +1 = male; -1 = female */
1042 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1045 if (!strncasecmp(options, "f", 1))
1047 else if (!strncasecmp(options, "m", 1))
1051 while (!res && num) {
1053 ast_copy_string(fn, "digits/minus", sizeof(fn));
1054 if ( num > INT_MIN ) {
1060 ast_copy_string(fn, "digits/and", sizeof(fn));
1062 } else if (num == 1) {
1064 snprintf(fn, sizeof(fn), "digits/%dF", num);
1066 snprintf(fn, sizeof(fn), "digits/%dM", num);
1068 snprintf(fn, sizeof(fn), "digits/%d", num);
1070 } else if (num < 31) {
1071 snprintf(fn, sizeof(fn), "digits/%d", num);
1073 } else if (num < 100) {
1074 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1078 } else if (num == 100) {
1079 ast_copy_string(fn, "digits/100", sizeof(fn));
1081 } else if (num < 200) {
1082 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1086 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1088 } else if (num < 2000) {
1090 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1092 if (num < 1000000) {
1093 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1097 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1099 if (num < 2147483640) {
1100 if ((num/1000000) == 1) {
1101 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1104 ast_copy_string(fn, "digits/million", sizeof(fn));
1106 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1109 ast_copy_string(fn, "digits/millions", sizeof(fn));
1113 ast_debug(1, "Number '%d' is too big for me\n", num);
1121 if (!ast_streamfile(chan, fn, language)) {
1122 if ((audiofd > -1) && (ctrlfd > -1))
1123 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1125 res = ast_waitstream(chan, ints);
1127 ast_stopstream(chan);
1135 /*! \brief ast_say_number_full_fr: French syntax
1136 Extra sounds needed:
1139 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)
1144 int mf = 1; /* +1 = male; -1 = female */
1147 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1149 if (options && !strncasecmp(options, "f", 1))
1152 while (!res && (num || playh || playa)) {
1154 ast_copy_string(fn, "digits/minus", sizeof(fn));
1155 if ( num > INT_MIN ) {
1161 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1164 ast_copy_string(fn, "digits/et", sizeof(fn));
1166 } else if (num == 1) {
1168 snprintf(fn, sizeof(fn), "digits/%dF", num);
1170 snprintf(fn, sizeof(fn), "digits/%d", num);
1172 } else if (num < 21) {
1173 snprintf(fn, sizeof(fn), "digits/%d", num);
1175 } else if (num < 70) {
1176 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1177 if ((num % 10) == 1) playa++;
1179 } else if (num < 80) {
1180 ast_copy_string(fn, "digits/60", sizeof(fn));
1181 if ((num % 10) == 1) playa++;
1183 } else if (num < 100) {
1184 ast_copy_string(fn, "digits/80", sizeof(fn));
1186 } else if (num < 200) {
1187 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1189 } else if (num < 1000) {
1190 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1193 } else if (num < 2000) {
1194 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1196 } else if (num < 1000000) {
1197 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1200 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1202 } else if (num < 1000000000) {
1203 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1206 ast_copy_string(fn, "digits/million", sizeof(fn));
1207 num = num % 1000000;
1209 ast_debug(1, "Number '%d' is too big for me\n", num);
1213 if (!ast_streamfile(chan, fn, language)) {
1214 if ((audiofd > -1) && (ctrlfd > -1))
1215 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1217 res = ast_waitstream(chan, ints);
1219 ast_stopstream(chan);
1228 * Check doc/lang/hebrew-digits.txt for information about the various
1229 * recordings required to make this translation work properly */
1230 #define SAY_NUM_BUF_SIZE 256
1231 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)
1234 int state = 0; /* no need to save anything */
1235 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1238 char fn[SAY_NUM_BUF_SIZE] = "";
1240 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1243 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1245 if (options && !strncasecmp(options, "m", 1)) {
1248 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1250 /* Do we have work to do? */
1251 while (!res && (num || (state > 0))) {
1252 /* first type of work: play a second sound. In this loop
1253 * we can only play one sound file at a time. Thus playing
1254 * a second one requires repeating the loop just for the
1255 * second file. The variable 'state' remembers where we were.
1256 * state==0 is the normal mode and it means that we continue
1257 * to check if the number num has yet anything left.
1259 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1263 } else if (state == 2) {
1264 if ((num >= 11) && (num < 21)) {
1266 snprintf(fn, sizeof(fn), "digits/ve");
1268 snprintf(fn, sizeof(fn), "digits/uu");
1273 snprintf(fn, sizeof(fn), "digits/ve");
1276 snprintf(fn, sizeof(fn), "digits/uu");
1280 snprintf(fn, sizeof(fn), "digits/ve");
1282 snprintf(fn, sizeof(fn), "digits/uu");
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/ve");
1298 snprintf(fn, sizeof(fn), "digits/uu");
1301 snprintf(fn, sizeof(fn), "digits/ve");
1304 snprintf(fn, sizeof(fn), "digits/ve");
1309 } else if (state == 3) {
1310 snprintf(fn, sizeof(fn), "digits/1k");
1312 } else if (num < 0) {
1313 snprintf(fn, sizeof(fn), "digits/minus");
1315 } else if (num < 20) {
1317 snprintf(fn, sizeof(fn), "digits/%d", num);
1319 snprintf(fn, sizeof(fn), "digits/%dm", num);
1322 } else if ((num < 100) && (num >= 20)) {
1323 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1328 } else if ((num >= 100) && (num < 1000)) {
1330 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1331 num = num - (tmpnum * 100);
1332 if ((num > 0) && (num < 11)) {
1335 } else if ((num >= 1000) && (num < 10000)) {
1336 tmpnum = num / 1000;
1337 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1338 num = num - (tmpnum * 1000);
1339 if ((num > 0) && (num < 11)) {
1342 } else if (num < 20000) {
1343 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1346 } else if (num < 1000000) {
1347 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1351 snprintf(fn, sizeof(fn), "digits/1k");
1353 if ((num > 0) && (num < 11)) {
1356 } else if (num < 2000000) {
1357 snprintf(fn, sizeof(fn), "digits/million");
1358 num = num % 1000000;
1359 if ((num > 0) && (num < 11)) {
1362 } else if (num < 3000000) {
1363 snprintf(fn, sizeof(fn), "digits/twomillion");
1364 num = num - 2000000;
1365 if ((num > 0) && (num < 11)) {
1368 } else if (num < 1000000000) {
1369 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1373 snprintf(fn, sizeof(fn), "digits/million");
1374 num = num % 1000000;
1375 if ((num > 0) && (num < 11)) {
1379 ast_debug(1, "Number '%d' is too big for me\n", num);
1384 if (!ast_streamfile(chan, fn, language)) {
1385 if ((audiofd > -1) && (ctrlfd > -1)) {
1386 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1388 res = ast_waitstream(chan, ints);
1391 ast_stopstream(chan);
1397 /*! \brief ast_say_number_full_hu: Hungarian syntax
1399 Extra sounds needed:
1403 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1409 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1413 like english, except numbers up to 29 are from 2 words.
1414 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1417 while(!res && (num || playh)) {
1419 ast_copy_string(fn, "digits/minus", sizeof(fn));
1420 if ( num > INT_MIN ) {
1426 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1428 } else if (num < 11 || num == 20) {
1429 snprintf(fn, sizeof(fn), "digits/%d", num);
1431 } else if (num < 20) {
1432 ast_copy_string(fn, "digits/10en", sizeof(fn));
1434 } else if (num < 30) {
1435 ast_copy_string(fn, "digits/20on", sizeof(fn));
1437 } else if (num < 100) {
1438 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1442 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1446 if (num < 1000000) { /* 1,000,000 */
1447 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1451 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1453 if (num < 1000000000) { /* 1,000,000,000 */
1454 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1458 ast_copy_string(fn, "digits/million", sizeof(fn));
1460 ast_debug(1, "Number '%d' is too big for me\n", num);
1467 if(!ast_streamfile(chan, fn, language)) {
1468 if ((audiofd > -1) && (ctrlfd > -1))
1469 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1471 res = ast_waitstream(chan, ints);
1473 ast_stopstream(chan);
1479 /*! \brief ast_say_number_full_it: Italian */
1480 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1488 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1493 Like english, numbers up to 20 are a single 'word', and others
1494 compound, but with exceptions.
1495 For example 21 is not twenty-one, but there is a single word in 'it'.
1496 Idem for 28 (ie when a the 2nd part of a compund number
1497 starts with a vowel)
1499 There are exceptions also for hundred, thousand and million.
1500 In english 100 = one hundred, 200 is two hundred.
1501 In italian 100 = cento , like to say hundred (without one),
1502 200 and more are like english.
1504 Same applies for thousand:
1505 1000 is one thousand in en, 2000 is two thousand.
1506 In it we have 1000 = mille , 2000 = 2 mila
1508 For million(s) we use the plural, if more than one
1509 Also, one million is abbreviated in it, like on-million,
1510 or 'un milione', not 'uno milione'.
1511 So the right file is provided.
1514 while (!res && (num || playh)) {
1516 ast_copy_string(fn, "digits/minus", sizeof(fn));
1517 if ( num > INT_MIN ) {
1523 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1525 } else if (num < 20) {
1526 snprintf(fn, sizeof(fn), "digits/%d", num);
1528 } else if (num == 21) {
1529 snprintf(fn, sizeof(fn), "digits/%d", num);
1531 } else if (num == 28) {
1532 snprintf(fn, sizeof(fn), "digits/%d", num);
1534 } else if (num == 31) {
1535 snprintf(fn, sizeof(fn), "digits/%d", num);
1537 } else if (num == 38) {
1538 snprintf(fn, sizeof(fn), "digits/%d", num);
1540 } else if (num == 41) {
1541 snprintf(fn, sizeof(fn), "digits/%d", num);
1543 } else if (num == 48) {
1544 snprintf(fn, sizeof(fn), "digits/%d", num);
1546 } else if (num == 51) {
1547 snprintf(fn, sizeof(fn), "digits/%d", num);
1549 } else if (num == 58) {
1550 snprintf(fn, sizeof(fn), "digits/%d", num);
1552 } else if (num == 61) {
1553 snprintf(fn, sizeof(fn), "digits/%d", num);
1555 } else if (num == 68) {
1556 snprintf(fn, sizeof(fn), "digits/%d", num);
1558 } else if (num == 71) {
1559 snprintf(fn, sizeof(fn), "digits/%d", num);
1561 } else if (num == 78) {
1562 snprintf(fn, sizeof(fn), "digits/%d", num);
1564 } else if (num == 81) {
1565 snprintf(fn, sizeof(fn), "digits/%d", num);
1567 } else if (num == 88) {
1568 snprintf(fn, sizeof(fn), "digits/%d", num);
1570 } else if (num == 91) {
1571 snprintf(fn, sizeof(fn), "digits/%d", num);
1573 } else if (num == 98) {
1574 snprintf(fn, sizeof(fn), "digits/%d", num);
1576 } else if (num < 100) {
1577 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1581 if ((num / 100) > 1) {
1582 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1585 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1589 if (num < 1000000) { /* 1,000,000 */
1591 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1596 if ((tempnum / 1000) < 2)
1597 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1598 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1599 ast_copy_string(fn, "digits/thousands", sizeof(fn));
1601 if (num < 1000000000) { /* 1,000,000,000 */
1602 if ((num / 1000000) > 1)
1603 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1608 if ((tempnum / 1000000) < 2)
1609 ast_copy_string(fn, "digits/million", sizeof(fn));
1611 ast_copy_string(fn, "digits/millions", sizeof(fn));
1613 ast_debug(1, "Number '%d' is too big for me\n", num);
1620 if (!ast_streamfile(chan, fn, language)) {
1621 if ((audiofd > -1) && (ctrlfd > -1))
1622 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1624 res = ast_waitstream(chan, ints);
1626 ast_stopstream(chan);
1632 /*! \brief ast_say_number_full_nl: dutch syntax
1633 * New files: digits/nl-en
1635 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1642 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1643 while (!res && (num || playh )) {
1645 ast_copy_string(fn, "digits/minus", sizeof(fn));
1646 if ( num > INT_MIN ) {
1652 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1654 } else if (num < 20) {
1655 snprintf(fn, sizeof(fn), "digits/%d", num);
1657 } else if (num < 100) {
1660 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1664 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
1666 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1669 } else if (num < 200) {
1670 /* hundred, not one-hundred */
1671 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1673 } else if (num < 1000) {
1674 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1679 /* thousand, not one-thousand */
1681 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1682 } else if (num < 10000) { /* 1,100 to 9,9999 */
1683 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1687 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1689 if (num < 1000000) { /* 1,000,000 */
1690 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1694 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1696 if (num < 1000000000) { /* 1,000,000,000 */
1697 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1701 ast_copy_string(fn, "digits/million", sizeof(fn));
1703 ast_debug(1, "Number '%d' is too big for me\n", num);
1711 if (!ast_streamfile(chan, fn, language)) {
1712 if ((audiofd > -1) && (ctrlfd > -1))
1713 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1715 res = ast_waitstream(chan, ints);
1717 ast_stopstream(chan);
1723 /*! \brief ast_say_number_full_no: Norwegian syntax
1725 * In addition to American English, the following sounds are required: "and", "1N"
1727 * The grammar for Norwegian numbers is the same as for English except
1728 * for the following:
1729 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1730 * "and" before the last two digits, i.e. 2034 is "two thousand and
1731 * thirty-four" and 1000012 is "one million and twelve".
1733 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)
1738 int cn = 1; /* +1 = commune; -1 = neuter */
1742 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1744 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1746 while (!res && (num || playh || playa )) {
1748 ast_copy_string(fn, "digits/minus", sizeof(fn));
1749 if ( num > INT_MIN ) {
1755 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1758 ast_copy_string(fn, "digits/and", sizeof(fn));
1760 } else if (num == 1 && cn == -1) {
1761 ast_copy_string(fn, "digits/1N", sizeof(fn));
1763 } else if (num < 20) {
1764 snprintf(fn, sizeof(fn), "digits/%d", num);
1766 } else if (num < 100) {
1767 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1769 } else if (num < 1000) {
1770 int hundreds = num / 100;
1772 ast_copy_string(fn, "digits/1N", sizeof(fn));
1774 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1777 num -= 100 * hundreds;
1780 } else if (num < 1000000) {
1781 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1784 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1786 if (num && num < 100)
1788 } else if (num < 1000000000) {
1789 int millions = num / 1000000;
1790 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1793 ast_copy_string(fn, "digits/million", sizeof(fn));
1795 if (num && num < 100)
1798 ast_debug(1, "Number '%d' is too big for me\n", num);
1803 if (!ast_streamfile(chan, fn, language)) {
1804 if ((audiofd > -1) && (ctrlfd > -1))
1805 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1807 res = ast_waitstream(chan, ints);
1809 ast_stopstream(chan);
1816 char *separator_dziesiatek;
1820 char *dziesiatki[10];
1825 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1831 return odm->rzedy[rzad - 1][0];
1832 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1833 return odm->rzedy[rzad - 1][1];
1835 return odm->rzedy[rzad - 1][2];
1838 static char* pl_append(char* buffer, char* str)
1840 strcpy(buffer, str);
1841 buffer += strlen(str);
1845 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1847 char file_name[255] = "digits/";
1848 strcat(file_name, fn);
1849 ast_debug(1, "Trying to play: %s\n", file_name);
1850 if (!ast_streamfile(chan, file_name, language)) {
1851 if ((audiofd > -1) && (ctrlfd > -1))
1852 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1854 ast_waitstream(chan, ints);
1856 ast_stopstream(chan);
1859 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1861 /* Initialise variables to allow compilation on Debian-stable, etc */
1871 if (i == 0 && rzad > 0) {
1875 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1879 m1000E6 = i % 1000000000;
1880 i1000E6 = i / 1000000000;
1882 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1884 m1000E3 = m1000E6 % 1000000;
1885 i1000E3 = m1000E6 / 1000000;
1887 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1889 m1000 = m1000E3 % 1000;
1890 i1000 = m1000E3 / 1000;
1892 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1898 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1900 if ( m100 > 0 && m100 <=9 ) {
1902 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1904 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1905 } else if (m100 % 10 == 0) {
1906 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1907 } else if (m100 <= 19 ) {
1908 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1909 } else if (m100 != 0) {
1910 if (odm->separator_dziesiatek[0]==' ') {
1911 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1912 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1916 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1917 b = pl_append(b, odm->separator_dziesiatek);
1918 b = pl_append(b, odm->cyfry2[m100 % 10]);
1919 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1924 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1928 /* ast_say_number_full_pl: Polish syntax
1938 1000000000.2 miliardy
1939 1000000000.5 miliardow
2003 70m siedemdziesieciu
2015 90m dziewiedziesieciu
2017 and combinations of eg.: 20_1, 30m_3m, etc...
2020 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)
2022 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2024 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2026 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2028 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2030 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2032 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2034 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2036 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2038 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2040 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2042 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2044 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2046 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2048 /* Initialise variables to allow compilation on Debian-stable, etc */
2051 static odmiana *odmiana_nieosobowa = NULL;
2052 static odmiana *odmiana_meska = NULL;
2053 static odmiana *odmiana_zenska = NULL;
2055 if (odmiana_nieosobowa == NULL) {
2056 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2058 odmiana_nieosobowa->separator_dziesiatek = " ";
2060 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2061 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2062 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2063 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2064 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2065 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2068 if (odmiana_zenska == NULL) {
2069 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2071 odmiana_zenska->separator_dziesiatek = " ";
2073 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2074 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2075 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2076 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2077 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2078 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2081 if (odmiana_meska == NULL) {
2082 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2084 odmiana_meska->separator_dziesiatek = " ";
2086 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2087 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2088 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2089 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2090 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2091 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2095 if (strncasecmp(options, "f", 1) == 0)
2097 else if (strncasecmp(options, "m", 1) == 0)
2100 o = odmiana_nieosobowa;
2102 o = odmiana_nieosobowa;
2104 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2108 /* ast_say_number_full_pt: Portuguese syntax
2110 * Extra sounds needed:
2111 * For feminin all sound files ends with F
2112 * 100E for 100+ something
2113 * 1000000S for plural
2116 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)
2120 int mf = 1; /* +1 = male; -1 = female */
2124 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2126 if (options && !strncasecmp(options, "f", 1))
2129 while (!res && num ) {
2131 ast_copy_string(fn, "digits/minus", sizeof(fn));
2132 if ( num > INT_MIN ) {
2137 } else if (num < 20) {
2138 if ((num == 1 || num == 2) && (mf < 0))
2139 snprintf(fn, sizeof(fn), "digits/%dF", num);
2141 snprintf(fn, sizeof(fn), "digits/%d", num);
2143 } else if (num < 100) {
2144 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2148 } else if (num < 1000) {
2150 ast_copy_string(fn, "digits/100", sizeof(fn));
2152 ast_copy_string(fn, "digits/100E", sizeof(fn));
2154 if (mf < 0 && num > 199)
2155 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2157 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2162 } else if (num < 1000000) {
2164 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2168 ast_copy_string(fn, "digits/1000", sizeof(fn));
2169 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2172 } else if (num < 1000000000) {
2173 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2177 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2179 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2181 if ((num % 1000000) &&
2183 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2184 /* no hundreds and below */
2185 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2187 num = num % 1000000;
2189 /* number is too big */
2190 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2194 if (!ast_streamfile(chan, fn, language)) {
2195 if ((audiofd > -1) && (ctrlfd > -1))
2196 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2198 res = ast_waitstream(chan, ints);
2200 ast_stopstream(chan);
2202 if (!res && playh) {
2203 res = wait_file(chan, ints, "digits/pt-e", language);
2204 ast_stopstream(chan);
2211 /*! \brief ast_say_number_full_se: Swedish syntax
2216 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)
2221 int cn = 1; /* +1 = commune; -1 = neuter */
2225 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2227 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2229 while (num || playh) {
2231 ast_copy_string(fn, "digits/minus", sizeof(fn));
2232 if ( num > INT_MIN ) {
2238 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2240 } else if (start && num < 200 && num > 99 && cn == -1) {
2241 /* Don't say "en hundra" just say "hundra". */
2242 snprintf(fn, sizeof(fn), "digits/hundred");
2244 } else if (num == 1 && cn == -1) { /* En eller ett? */
2245 ast_copy_string(fn, "digits/1N", sizeof(fn));
2247 } else if (num < 20) {
2248 snprintf(fn, sizeof(fn), "digits/%d", num);
2250 } else if (num < 100) { /* Below hundreds - teens and tens */
2251 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2253 } else if (num < 1000) {
2255 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2258 } else if (num < 1000000) { /* 1,000,000 */
2259 /* Always say "ett hundra tusen", not "en hundra tusen" */
2260 res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2265 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2266 } else if (num < 1000000000) { /* 1,000,000,000 */
2267 /* Always say "en miljon", not "ett miljon" */
2268 res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2273 ast_copy_string(fn, "digits/million", sizeof(fn));
2274 } else { /* Miljarder - Billions */
2275 ast_debug(1, "Number '%d' is too big for me\n", num);
2279 if (!ast_streamfile(chan, fn, language)) {
2280 if ((audiofd > -1) && (ctrlfd > -1)) {
2281 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2283 res = ast_waitstream(chan, ints);
2285 ast_stopstream(chan);
2295 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2296 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2302 int last_length = 0;
2306 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2308 while (!res && (num || playh || playt || playz)) {
2310 ast_copy_string(fn, "digits/minus", sizeof(fn));
2311 if ( num > INT_MIN ) {
2317 snprintf(fn, sizeof(fn), "digits/0");
2321 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2324 snprintf(fn, sizeof(fn), "digits/thousand");
2326 } else if (num < 10) {
2327 snprintf(buf, 10, "%d", num);
2328 if (last_length - strlen(buf) > 1 && last_length != 0) {
2329 last_length = strlen(buf);
2333 snprintf(fn, sizeof(fn), "digits/%d", num);
2335 } else if (num < 100) {
2336 snprintf(buf, 10, "%d", num);
2337 if (last_length - strlen(buf) > 1 && last_length != 0) {
2338 last_length = strlen(buf);
2342 last_length = strlen(buf);
2343 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2347 snprintf(buf, 10, "%d", num);
2348 if (last_length - strlen(buf) > 1 && last_length != 0) {
2349 last_length = strlen(buf);
2353 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2355 snprintf(buf, 10, "%d", num);
2356 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2357 last_length = strlen(buf);
2358 num -= ((num / 100) * 100);
2359 } else if (num < 10000){
2360 snprintf(buf, 10, "%d", num);
2361 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2363 snprintf(buf, 10, "%d", num);
2364 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2365 last_length = strlen(buf);
2366 num -= ((num / 1000) * 1000);
2367 } else if (num < 100000000) { /* 100,000,000 */
2368 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2371 snprintf(buf, 10, "%d", num);
2372 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2373 num -= ((num / 10000) * 10000);
2374 last_length = strlen(buf);
2375 snprintf(fn, sizeof(fn), "digits/wan");
2377 if (num < 1000000000) { /* 1,000,000,000 */
2378 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2381 snprintf(buf, 10, "%d", num);
2382 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2383 last_length = strlen(buf);
2384 num -= ((num / 100000000) * 100000000);
2385 snprintf(fn, sizeof(fn), "digits/yi");
2387 ast_debug(1, "Number '%d' is too big for me\n", num);
2393 if (!ast_streamfile(chan, fn, language)) {
2394 if ((audiofd > -1) && (ctrlfd > -1))
2395 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2397 res = ast_waitstream(chan, ints);
2399 ast_stopstream(chan);
2406 * \brief Counting in Urdu, the national language of Pakistan
2409 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)
2416 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2419 while (!res && (num || playh)) {
2421 snprintf(fn, sizeof(fn), "digits/hundred");
2423 } else if (num < 100) {
2424 snprintf(fn, sizeof(fn), "digits/%d", num);
2426 } else if (num < 1000) {
2427 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2429 num -= ((num / 100) * 100);
2430 } else if (num < 100000) { /* 1,00,000 */
2431 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2435 snprintf(fn, sizeof(fn), "digits/thousand");
2436 } else if (num < 10000000) { /* 1,00,00,000 */
2437 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2441 snprintf(fn, sizeof(fn), "digits/lac");
2442 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2443 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2446 num = num % 10000000;
2447 snprintf(fn, sizeof(fn), "digits/crore");
2449 ast_debug(1, "Number '%d' is too big for me\n", num);
2454 if (!ast_streamfile(chan, fn, language)) {
2455 if ((audiofd > -1) && (ctrlfd > -1)) {
2456 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2458 res = ast_waitstream(chan, ints);
2461 ast_stopstream(chan);
2467 /*! \brief determine last digits for thousands/millions (ru) */
2468 static int get_lastdigits_ru(int num) {
2471 } else if (num < 100) {
2472 return get_lastdigits_ru(num % 10);
2473 } else if (num < 1000) {
2474 return get_lastdigits_ru(num % 100);
2476 return 0; /* number too big */
2480 /*! \brief ast_say_number_full_ru: Russian syntax
2483 n00.gsm (one hundred, two hundred, ...)
2486 thousands-i.gsm (tisyachi)
2487 million-a.gsm (milliona)
2493 where 'n' from 1 to 9
2495 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)
2501 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2503 while (!res && (num)) {
2505 ast_copy_string(fn, "digits/minus", sizeof(fn));
2506 if ( num > INT_MIN ) {
2511 } else if (num < 20) {
2512 if (options && strlen(options) == 1 && num < 3) {
2513 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2515 snprintf(fn, sizeof(fn), "digits/%d", num);
2518 } else if (num < 100) {
2519 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2521 } else if (num < 1000){
2522 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2524 } else if (num < 1000000) { /* 1,000,000 */
2525 lastdigits = get_lastdigits_ru(num / 1000);
2527 if (lastdigits < 3) {
2528 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2530 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2534 if (lastdigits == 1) {
2535 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2536 } else if (lastdigits > 1 && lastdigits < 5) {
2537 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2539 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2542 } else if (num < 1000000000) { /* 1,000,000,000 */
2543 lastdigits = get_lastdigits_ru(num / 1000000);
2545 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2548 if (lastdigits == 1) {
2549 ast_copy_string(fn, "digits/million", sizeof(fn));
2550 } else if (lastdigits > 1 && lastdigits < 5) {
2551 ast_copy_string(fn, "digits/million-a", sizeof(fn));
2553 ast_copy_string(fn, "digits/millions", sizeof(fn));
2557 ast_debug(1, "Number '%d' is too big for me\n", num);
2561 if (!ast_streamfile(chan, fn, language)) {
2562 if ((audiofd > -1) && (ctrlfd > -1))
2563 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2565 res = ast_waitstream(chan, ints);
2567 ast_stopstream(chan);
2573 /*! \brief Thai syntax */
2574 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2580 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2582 while(!res && (num || playh)) {
2584 ast_copy_string(fn, "digits/lop", sizeof(fn));
2585 if ( num > INT_MIN ) {
2591 ast_copy_string(fn, "digits/roi", sizeof(fn));
2593 } else if (num < 100) {
2594 if ((num <= 20) || ((num % 10) == 1)) {
2595 snprintf(fn, sizeof(fn), "digits/%d", num);
2598 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2601 } else if (num < 1000) {
2602 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2605 } else if (num < 10000) { /* 10,000 */
2606 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2610 ast_copy_string(fn, "digits/pan", sizeof(fn));
2611 } else if (num < 100000) { /* 100,000 */
2612 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2616 ast_copy_string(fn, "digits/muan", sizeof(fn));
2617 } else if (num < 1000000) { /* 1,000,000 */
2618 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2622 ast_copy_string(fn, "digits/san", sizeof(fn));
2624 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2628 ast_copy_string(fn, "digits/larn", sizeof(fn));
2631 if(!ast_streamfile(chan, fn, language)) {
2632 if ((audiofd > -1) && (ctrlfd > -1))
2633 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2635 res = ast_waitstream(chan, ints);
2637 ast_stopstream(chan);
2643 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
2644 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2654 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2655 while (!res && (num || playh)) {
2657 ast_copy_string(fn, "digits/minus", sizeof(fn));
2658 if ( num > INT_MIN ) {
2664 snprintf(fn, sizeof(fn), "digits/%da", num);
2668 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2671 ast_copy_string(fn, "digits/odd", sizeof(fn));
2673 } else if (playoh) {
2674 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
2676 } else if (playohz) {
2677 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
2679 } else if (num < 20) {
2680 snprintf(fn, sizeof(fn), "digits/%d", num);
2682 } else if (num < 100) {
2683 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2685 if ((num == 5) || (num == 4) || (num == 1)) playl++;
2688 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2690 if (num && (num < 10)) {
2697 if (num < 1000000) { /* 1,000,000 */
2698 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
2702 snprintf(fn, sizeof(fn), "digits/thousand");
2703 if (num && (num < 10)) {
2705 } else if (num && (num < 100)){
2713 if (num < 1000000000) { /* 1,000,000,000 */
2714 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2718 ast_copy_string(fn, "digits/million", sizeof(fn));
2726 if (!ast_streamfile(chan, fn, language)) {
2727 if ((audiofd > -1) && (ctrlfd > -1))
2728 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2730 res = ast_waitstream(chan, ints);
2732 ast_stopstream(chan);
2738 /*! \brief ast_say_enumeration_full: call language-specific functions
2739 * \note Called from AGI */
2740 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2742 if (!strncasecmp(language, "en", 2)) { /* English syntax */
2743 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2744 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
2745 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
2746 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
2747 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
2748 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
2749 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
2750 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
2751 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
2754 /* Default to english */
2755 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2758 /*! \brief ast_say_enumeration_full_en: English syntax
2759 \note This is the default syntax, if no other syntax defined in this file is used */
2760 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2765 while (!res && num) {
2767 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2768 if ( num > INT_MIN ) {