Asterisk - The Open Source Telephony Project GIT-master-d856a3e
say.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2005, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 * George Konstantoulakis <gkon@inaccessnetworks.com>
8 *
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.
14 *
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.
18 */
19
20/*! \file
21 *
22 * \brief Say numbers and dates (maybe words one day too)
23 *
24 * \author Mark Spencer <markster@digium.com>
25 *
26 * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
27 *
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.
32 * \note 2021-07-26 : Refactoring to separate string buildup and playback
33 * by Naveen Albert <asterisk@phreaknet.org>
34 */
35
36/*** MODULEINFO
37 <support_level>core</support_level>
38 ***/
39
40#include "asterisk.h"
41
42#include <netinet/in.h>
43#include <time.h>
44#include <ctype.h>
45#include <math.h>
46
47#ifdef SOLARIS
48#include <iso/limits_iso.h>
49#endif
50
51#include "asterisk/file.h"
52#include "asterisk/channel.h"
53#include "asterisk/say.h"
54#include "asterisk/lock.h"
55#include "asterisk/localtime.h"
56#include "asterisk/utils.h"
57#include "asterisk/app.h"
58#include "asterisk/test.h"
59#include "asterisk/cli.h" /* use ESS */
60
61/* Forward declaration */
62static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
63
64struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
65 const char *fn;
66 char fnbuf[10], asciibuf[20] = "letters/ascii";
67 char ltr;
68 int num = 0;
69 int res = 0;
70 int upper = 0;
71 int lower = 0;
72
73 struct ast_str *filenames = ast_str_create(20);
74 if (!filenames) {
75 return NULL;
76 }
77 ast_str_reset(filenames);
78
79 while (str[num] && !res) {
80 fn = NULL;
81 switch (str[num]) {
82 case ('*'):
83 fn = "digits/star";
84 break;
85 case ('#'):
86 fn = "digits/pound";
87 break;
88 case ('!'):
89 fn = "letters/exclaimation-point";
90 break;
91 case ('@'):
92 fn = "letters/at";
93 break;
94 case ('$'):
95 fn = "letters/dollar";
96 break;
97 case ('-'):
98 fn = "letters/dash";
99 break;
100 case ('.'):
101 fn = "letters/dot";
102 break;
103 case ('='):
104 fn = "letters/equals";
105 break;
106 case ('+'):
107 fn = "letters/plus";
108 break;
109 case ('/'):
110 fn = "letters/slash";
111 break;
112 case (' '):
113 fn = "letters/space";
114 break;
115 case ('0'):
116 case ('1'):
117 case ('2'):
118 case ('3'):
119 case ('4'):
120 case ('5'):
121 case ('6'):
122 case ('7'):
123 case ('8'):
124 case ('9'):
125 strcpy(fnbuf, "digits/X");
126 fnbuf[7] = str[num];
127 fn = fnbuf;
128 break;
129 default:
130 ltr = str[num];
131 if ('A' <= ltr && ltr <= 'Z') {
132 ltr += 'a' - 'A'; /* file names are all lower-case */
133 switch (sensitivity) {
135 case AST_SAY_CASE_ALL:
136 upper = !upper;
139 break;
140 }
141 } else if ('a' <= ltr && ltr <= 'z') {
142 switch (sensitivity) {
144 case AST_SAY_CASE_ALL:
145 lower = !lower;
148 break;
149 }
150 }
151
152 if (upper) {
153 strcpy(fnbuf, "uppercase");
154 } else if (lower) {
155 strcpy(fnbuf, "lowercase");
156 } else {
157 strcpy(fnbuf, "letters/X");
158 fnbuf[8] = ltr;
159 }
160 fn = fnbuf;
161 }
162 if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
163 (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
164 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
165 }
166 if (upper || lower) {
167 continue;
168 }
169 num++;
170 }
171
172 return filenames;
173}
174
175static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)
176{
177 int res = 0;
178 char *files;
179 const char *fn;
180
181 if (!filenames) {
182 return -1;
183 }
184
185 /* No filenames to play? Return success so we don't hang up erroneously */
186 if (ast_str_strlen(filenames) == 0) {
187 ast_free(filenames);
188 return 0;
189 }
190
191 files = ast_str_buffer(filenames);
192
193 while (!res && (fn = strsep(&files, "&"))) {
194 res = ast_streamfile(chan, fn, lang);
195 if (!res) {
196 if ((audiofd > -1) && (ctrlfd > -1)) {
197 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
198 } else {
199 res = ast_waitstream(chan, ints);
200 }
201 }
202 ast_stopstream(chan);
203 }
204
205 ast_free(filenames);
206
207 return res;
208}
209
210static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)
211{
212 struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
213 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
214}
215
216struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
217{
218 const char *fn;
219 char fnbuf[256];
220 char ltr;
221 int num = 0;
222
223 struct ast_str *filenames = ast_str_create(20);
224 if (!filenames) {
225 return NULL;
226 }
227 ast_str_reset(filenames);
228
229 while (str[num]) {
230 fn = NULL;
231 switch (str[num]) {
232 case ('*'):
233 fn = "digits/star";
234 break;
235 case ('#'):
236 fn = "digits/pound";
237 break;
238 case ('!'):
239 fn = "letters/exclaimation-point";
240 break;
241 case ('@'):
242 fn = "letters/at";
243 break;
244 case ('$'):
245 fn = "letters/dollar";
246 break;
247 case ('-'):
248 fn = "letters/dash";
249 break;
250 case ('.'):
251 fn = "letters/dot";
252 break;
253 case ('='):
254 fn = "letters/equals";
255 break;
256 case ('+'):
257 fn = "letters/plus";
258 break;
259 case ('/'):
260 fn = "letters/slash";
261 break;
262 case (' '):
263 fn = "letters/space";
264 break;
265 case ('0'):
266 case ('1'):
267 case ('2'):
268 case ('3'):
269 case ('4'):
270 case ('5'):
271 case ('6'):
272 case ('7'):
273 case ('8'):
274 strcpy(fnbuf, "digits/X");
275 fnbuf[7] = str[num];
276 fn = fnbuf;
277 break;
278 default: /* '9' falls here... */
279 ltr = str[num];
280 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
281 strcpy(fnbuf, "phonetic/X_p");
282 fnbuf[9] = ltr;
283 fn = fnbuf;
284 }
285 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
286 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
287 }
288 num++;
289 }
290
291 return filenames;
292}
293
294static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
295{
296 struct ast_str *filenames = ast_get_phonetic_str(str, lang);
297 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
298}
299
300struct ast_str* ast_get_digit_str(const char *str, const char *lang)
301{
302 const char *fn;
303 char fnbuf[256];
304 int num = 0;
305
306 struct ast_str *filenames = ast_str_create(20);
307 if (!filenames) {
308 return NULL;
309 }
310 ast_str_reset(filenames);
311
312 while (str[num]) {
313 fn = NULL;
314 switch (str[num]) {
315 case ('*'):
316 fn = "digits/star";
317 break;
318 case ('#'):
319 fn = "digits/pound";
320 break;
321 case ('-'):
322 fn = "digits/minus";
323 break;
324 case '0':
325 case '1':
326 case '2':
327 case '3':
328 case '4':
329 case '5':
330 case '6':
331 case '7':
332 case '8':
333 case '9':
334 strcpy(fnbuf, "digits/X");
335 fnbuf[7] = str[num];
336 fn = fnbuf;
337 break;
338 }
339 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
340 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
341 }
342 num++;
343 }
344
345 return filenames;
346}
347
348static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
349{
350 struct ast_str *filenames = ast_get_digit_str(str, lang);
351 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
352}
353
354static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
355{
356 const char *fnr;
357 int amt, dollars = 0, cents = 0;
358 struct ast_str *fnrecurse = NULL;
359 struct ast_str *filenames;
360
361 if (ast_strlen_zero(str)) {
362 return NULL;
363 }
364
365 filenames = ast_str_create(20);
366 if (!filenames) {
367 return NULL;
368 }
369 ast_str_reset(filenames);
370
371 /* Don't use %f because floating point rounding
372 * could distort the cents units. Just parse as string. */
373 if (str && *str == '.') {
374 if (sscanf(str, ".%02u", &cents) < 1) {
375 dollars = cents = 0;
376 } else {
377 /* If we have a space instead of numbers after '.',
378 * then it's not quite valid. */
379 const char *period = strchr(str, '.');
380 if (period && !isdigit(*(period + 1))) {
381 cents = 0;
382 }
383 }
384 } else {
385 int res = sscanf(str, "%d.%02u", &dollars, &cents);
386 if (res < 1) {
387 dollars = cents = 0;
388 } else if (res == 2) {
389 const char *period = strchr(str, '.');
390 if (period && !isdigit(*(period + 1))) {
391 cents = 0;
392 }
393 }
394 }
395 amt = dollars * 100 + cents; /* convert everything to cents */
396
397 ast_debug(1, "Amount is %d (%d dollar%s, %d cent%s)\n", amt, dollars, ESS(dollars), cents, ESS(cents));
398
399 if (amt >= 100) {
400 fnrecurse = ast_get_number_str((amt / 100), lang);
401 if (!fnrecurse) {
402 ast_log(LOG_WARNING, "Couldn't get string for dollars\n");
403 } else {
404 fnr = ast_str_buffer(fnrecurse);
405 ast_str_append(&filenames, 0, "%s", fnr);
406 }
407
408 /* If this is it, end on a down pitch, otherwise up pitch */
409 if (amt < 200) {
410 ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
411 } else {
412 ast_str_append(&filenames, 0, "&%s", "dollars");
413 }
414
415 /* If dollars and cents, add "and" in the middle */
416 if (cents > 0) {
417 ast_str_append(&filenames, 0, "&%s", "and");
418 }
419 }
420
421 if (cents > 0) {
422 fnrecurse = ast_get_number_str(cents, lang);
423 if (!fnrecurse) {
424 ast_log(LOG_ERROR, "Couldn't get string for cents\n");
425 } else {
426 fnr = ast_str_buffer(fnrecurse);
427 ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
428 }
429 ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
430 } else if (amt == 0) {
431 fnrecurse = ast_get_digit_str("0", lang);
432 if (!fnrecurse) {
433 ast_log(LOG_ERROR, "Couldn't get string for cents\n");
434 } else {
435 fnr = ast_str_buffer(fnrecurse);
436 ast_str_append(&filenames, 0, "%s", fnr);
437 }
438 ast_str_append(&filenames, 0, "&%s", "cents");
439 }
440
441 if (fnrecurse) {
442 ast_free(fnrecurse);
443 }
444
445 return filenames;
446}
447
448/*! \brief ast_get_money_str: call language-specific functions */
449struct ast_str* ast_get_money_str(const char *str, const char *lang)
450{
451 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
452 return ast_get_money_en_dollars_str(str, lang);
453 }
454
455 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
456 /* Default to english */
457 return ast_get_money_en_dollars_str(str, lang);
458}
459
460static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
461{
462 struct ast_str *filenames = ast_get_money_str(str, lang);
463 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
464}
465
466static struct ast_str* get_number_str_en(int num, const char *lang)
467{
468 const char *fnr;
469 int loops = 0;
470
471 int res = 0;
472 int playh = 0;
473 char fn[256] = "";
474
475 struct ast_str *filenames;
476
477 if (!num) {
478 return ast_get_digit_str("0", lang);
479 }
480
481 filenames = ast_str_create(20);
482 if (!filenames) {
483 return NULL;
484 }
485 ast_str_reset(filenames);
486
487 while (!res && (num || playh)) {
488 if (num < 0) {
489 ast_copy_string(fn, "digits/minus", sizeof(fn));
490 if ( num > INT_MIN ) {
491 num = -num;
492 } else {
493 num = 0;
494 }
495 } else if (playh) {
496 ast_copy_string(fn, "digits/hundred", sizeof(fn));
497 playh = 0;
498 } else if (num < 20) {
499 snprintf(fn, sizeof(fn), "digits/%d", num);
500 num = 0;
501 } else if (num < 100) {
502 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
503 num %= 10;
504 } else {
505 if (num < 1000){
506 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
507 playh++;
508 num %= 100;
509 } else {
510 struct ast_str *fnrecurse = NULL;
511 if (num < 1000000) { /* 1,000,000 */
512 fnrecurse = get_number_str_en((num / 1000), lang);
513 if (!fnrecurse) {
514 ast_log(LOG_ERROR, "Couldn't get string for num\n");
515 } else {
516 fnr = ast_str_buffer(fnrecurse);
517 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
518 }
519 num %= 1000;
520 snprintf(fn, sizeof(fn), "&digits/thousand");
521 } else {
522 if (num < 1000000000) { /* 1,000,000,000 */
523 fnrecurse = get_number_str_en((num / 1000000), lang);
524 if (!fnrecurse) {
525 ast_log(LOG_ERROR, "Couldn't get string for num\n");
526 } else {
527 fnr = ast_str_buffer(fnrecurse);
528 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
529 }
530 num %= 1000000;
531 ast_copy_string(fn, "&digits/million", sizeof(fn));
532 } else {
533 if (num < INT_MAX) {
534 fnrecurse = get_number_str_en((num / 1000000000), lang);
535 if (!fnrecurse) {
536 ast_log(LOG_ERROR, "Couldn't get string for num\n");
537 } else {
538 fnr = ast_str_buffer(fnrecurse);
539 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
540 }
541 num %= 1000000000;
542 ast_copy_string(fn, "&digits/billion", sizeof(fn));
543 } else {
544 ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
545 res = -1;
546 }
547 }
548 }
549 if (fnrecurse) {
550 ast_free(fnrecurse);
551 }
552 /* we already decided whether or not to add an &, don't add another one immediately */
553 loops = 0;
554 }
555 }
556 if (!res) {
557 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
558 loops++;
559 }
560 }
561
562 return filenames;
563}
564
565/*! \brief ast_get_number_str: call language-specific functions */
566struct ast_str* ast_get_number_str(int num, const char *lang)
567{
568 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
569 return get_number_str_en(num, lang);
570 }
571
572 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
573 /* Default to english */
574 return get_number_str_en(num, lang);
575}
576
577static struct ast_str* get_ordinal_str_en(int num, const char *lang)
578{
579 const char *fnr;
580 int loops = 0;
581
582 int res = 0;
583 int playh = 0;
584 char fn[256] = "";
585
586 struct ast_str *filenames;
587
588 if (!num) {
589 num = 0;
590 }
591
592 filenames = ast_str_create(20);
593 if (!filenames) {
594 return NULL;
595 }
596 ast_str_reset(filenames);
597
598 while (!res && (num || playh)) {
599 if (num < 0) {
600 ast_copy_string(fn, "digits/minus", sizeof(fn));
601 if ( num > INT_MIN ) {
602 num = -num;
603 } else {
604 num = 0;
605 }
606 } else if (playh) {
607 ast_copy_string(fn, (num % 100 == 0) ? "digits/h-hundred" : "digits/hundred", sizeof(fn));
608 playh = 0;
609 } else if (num < 20) {
610 if (num > 0) {
611 snprintf(fn, sizeof(fn), "digits/h-%d", num);
612 } else {
613 ast_log(LOG_ERROR, "Unsupported ordinal number: %d\n", num);
614 }
615 num = 0;
616 } else if (num < 100) {
617 int base = (num / 10) * 10;
618 if (base != num) {
619 snprintf(fn, sizeof(fn), "digits/%d", base);
620 } else {
621 snprintf(fn, sizeof(fn), "digits/h-%d", base);
622 }
623 num %= 10;
624 } else {
625 if (num < 1000){
626 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
627 playh++;
628 num %= 100;
629 } else {
630 struct ast_str *fnrecurse = NULL;
631 if (num < 1000000) { /* 1,000,000 */
632 fnrecurse = get_number_str_en((num / 1000), lang);
633 if (!fnrecurse) {
634 ast_log(LOG_ERROR, "Couldn't get string for num\n");
635 } else {
636 fnr = ast_str_buffer(fnrecurse);
637 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
638 }
639 num %= 1000;
640 snprintf(fn, sizeof(fn), (num % 1000 == 0) ? "&digits/h-thousand" : "&digits/thousand");
641 } else {
642 if (num < 1000000000) { /* 1,000,000,000 */
643 fnrecurse = get_number_str_en((num / 1000000), lang);
644 if (!fnrecurse) {
645 ast_log(LOG_ERROR, "Couldn't get string for num\n");
646 } else {
647 fnr = ast_str_buffer(fnrecurse);
648 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
649 }
650 num %= 1000000;
651 ast_copy_string(fn, (num % 1000000 == 0) ? "&digits/h-million" : "&digits/million", sizeof(fn));
652 } else {
653 if (num < INT_MAX) {
654 fnrecurse = get_number_str_en((num / 1000000000), lang);
655 if (!fnrecurse) {
656 ast_log(LOG_ERROR, "Couldn't get string for num\n");
657 } else {
658 fnr = ast_str_buffer(fnrecurse);
659 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
660 }
661 num %= 1000000000;
662 ast_copy_string(fn, (num % 1000000000 == 0) ? "&digits/h-billion" : "&digits/billion", sizeof(fn));
663 } else {
664 ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
665 res = -1;
666 }
667 }
668 }
669 if (fnrecurse) {
670 ast_free(fnrecurse);
671 }
672 /* we already decided whether or not to add an &, don't add another one immediately */
673 loops = 0;
674 }
675 }
676 if (!res) {
677 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
678 loops++;
679 }
680 }
681
682 return filenames;
683}
684
685/*! \brief ast_get_ordinal_str: call language-specific functions */
686struct ast_str* ast_get_ordinal_str(int num, const char *lang)
687{
688 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
689 return get_ordinal_str_en(num, lang);
690 }
691
692 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
693 /* Default to english */
694 return get_ordinal_str_en(num, lang);
695}
696
697/* Forward declarations */
698/*! \page Def_syntaxlang Asterisk Language Syntaxes supported
699 \note Not really language codes.
700 For these language codes, Asterisk will change the syntax when
701 saying numbers (and in some cases dates and voicemail messages
702 as well)
703 \arg \b da - Danish
704 \arg \b de - German
705 \arg \b en - English (US)
706 \arg \b en_GB - English (British)
707 \arg \b es - Spanish, Mexican
708 \arg \b fr - French
709 \arg \b he - Hebrew
710 \arg \b is - Icelandic
711 \arg \b it - Italian
712 \arg \b nl - Dutch
713 \arg \b no - Norwegian
714 \arg \b pl - Polish
715 \arg \b pt - Portuguese
716 \arg \b pt_BR - Portuguese (Brazil)
717 \arg \b se - Swedish
718 \arg \b zh - Taiwanese / Chinese
719 \arg \b ru - Russian
720 \arg \b ka - Georgian
721 \arg \b hu - Hungarian
722
723 \par Gender:
724 For some languages the numbers differ for gender of the countable object.
725 Commonly for "one", like "un"/"une" in French. Note that the interface
726 is somewhat peculiar, as differing languages can have conflicting
727 genders.
728 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
729 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
730
731 Date/Time functions currently have less languages supported than saynumber().
732
733 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
734
735 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
736
737 \par Portuguese
738 Portuguese sound files needed for Time/Date functions:
739 pt-ah
740 pt-ao
741 pt-de
742 pt-e
743 pt-ora
744 pt-meianoite
745 pt-meiodia
746 pt-sss
747
748 \par Spanish
749 Spanish sound files needed for Time/Date functions:
750 es-de
751 es-el
752
753 \par Italian
754 Italian sound files needed for Time/Date functions:
755 ore-una
756 ore-mezzanotte
757
758*/
759
760/* Forward declarations of language specific variants of ast_say_number_full */
761static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
762static 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);
763static 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);
764static 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);
765static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
766static 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);
767static 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);
768static 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);
769static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
770static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
771static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
772static 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);
773static 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);
774static 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);
775static 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);
776static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
777static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
778static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
779static 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);
780static 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);
781static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
782static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
783static 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);
784static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
785
786/* Forward declarations of language specific variants of ast_say_enumeration_full */
787static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
788static 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);
789static 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);
790static 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);
791static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
792static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
793
794/* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
795static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
796static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
797static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
798static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
799static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
800static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
801static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
802static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
803static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
804static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
805static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
806static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
807static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
808
809static 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);
810static 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);
811static 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);
812static 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);
813static 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);
814static 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);
815static int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
816static 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);
817static 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);
818static 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);
819static 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);
820static 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);
821static 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);
822static int ast_say_date_with_format_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
823static 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);
824static 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);
825
826static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
827static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
828static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
829static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
830static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
831static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
832static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
833static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
834static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
835static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
836static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
837static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
838static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
839
840static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
841static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
842static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
843static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
844static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
845static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
846static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
847static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
848static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
849static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
850static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
851static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
852static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
853
854static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
855static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
856static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
857static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
858static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
859
860static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
861{
862 int res;
863 if ((res = ast_streamfile(chan, file, lang))) {
864 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
865 }
866 if (!res) {
867 res = ast_waitstream(chan, ints);
868 }
869 return res;
870}
871
872/*! \brief ast_say_number_full: call language-specific functions
873 \note Called from AGI */
874static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
875{
876 ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
877 if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
878 return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
879 } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
880 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
881 } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
882 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
883 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
884 return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
885 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
886 return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
887 } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
888 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
889 } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
890 return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
891 } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
892 return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
893 } else if (!strncasecmp(language, "ja", 2)) { /* Japanese syntax */
894 return ast_say_number_full_ja(chan, num, ints, language, audiofd, ctrlfd);
895 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
896 return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
897 } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
898 return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
899 } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
900 return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
901 } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
902 return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
903 } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
904 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
905 } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
906 return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
907 } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
908 return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
909 } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
910 return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
911 } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
912 return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
913 } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
914 return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
915 } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
916 return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
917 } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
918 return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
919 } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
920 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
921 } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
922 return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
923 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
924 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
925 }
926
927 /* Default to english */
928 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
929}
930
931/*! \brief ast_say_number_full_en: English syntax
932 \note This is the default syntax, if no other syntax defined in this file is used */
933static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
934{
935 struct ast_str *filenames = ast_get_number_str(num, language);
936 return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
937}
938
939/*! \brief say_ordinal_full */
940static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
941{
942 struct ast_str *filenames = ast_get_ordinal_str(num, language);
943 return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
944}
945
946static int exp10_int(int power)
947{
948 int x, res= 1;
949 for (x=0;x<power;x++)
950 res *= 10;
951 return res;
952}
953
954/*! \brief ast_say_number_full_cs: Czech syntax
955 *
956 * files needed:
957 * - 1m,2m - gender male
958 * - 1w,2w - gender female
959 * - 3,4,...,20
960 * - 30,40,...,90
961 *
962 * - hundreds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
963 *
964 * for each number 10^(3n + 3) exist 3 files represented as:
965 * 1 thousand = jeden tisic = 1_E3
966 * 2,3,4 thousands = dva,tri,ctyri tisice = 2-3_E3
967 * 5,6,... thousands = pet,sest,... tisic = 5_E3
968 *
969 * million = _E6
970 * miliard = _E9
971 * etc...
972 *
973 * thousand, milion are gender male, so 1 and 2 is 1m 2m
974 * miliard is gender female, so 1 and 2 is 1w 2w
975 */
976static 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)
977{
978 int res = 0;
979 int playh = 0;
980 char fn[256] = "";
981
982 int hundred = 0;
983 int left = 0;
984 int length = 0;
985
986 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
987 if (!options)
988 options = "w";
989
990 if (!num)
991 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
992
993 while (!res && (num || playh)) {
994 if (num < 0) {
995 ast_copy_string(fn, "digits/minus", sizeof(fn));
996 if ( num > INT_MIN ) {
997 num = -num;
998 } else {
999 num = 0;
1000 }
1001 } else if (num < 3 ) {
1002 snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
1003 playh = 0;
1004 num = 0;
1005 } else if (num < 20) {
1006 snprintf(fn, sizeof(fn), "digits/%d", num);
1007 playh = 0;
1008 num = 0;
1009 } else if (num < 100) {
1010 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1011 num %= 10;
1012 } else if (num < 1000) {
1013 hundred = num / 100;
1014 if ( hundred == 1 ) {
1015 ast_copy_string(fn, "digits/1sto", sizeof(fn));
1016 } else if ( hundred == 2 ) {
1017 ast_copy_string(fn, "digits/2ste", sizeof(fn));
1018 } else {
1019 res = ast_say_number_full_cs(chan, hundred, ints, language, options, audiofd, ctrlfd);
1020 if (res)
1021 return res;
1022 if (hundred == 3 || hundred == 4) {
1023 ast_copy_string(fn, "digits/sta", sizeof(fn));
1024 } else if ( hundred > 4 ) {
1025 ast_copy_string(fn, "digits/set", sizeof(fn));
1026 }
1027 }
1028 num -= (hundred * 100);
1029 } else { /* num > 1000 */
1030 length = (int)log10(num)+1;
1031 while ( (length % 3 ) != 1 ) {
1032 length--;
1033 }
1034 left = num / (exp10_int(length-1));
1035 if ( left == 2 ) {
1036 switch (length-1) {
1037 case 9: options = "w"; /* 1,000,000,000 gender female */
1038 break;
1039 default : options = "m"; /* others are male */
1040 }
1041 }
1042 if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
1043 res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
1044 if (res)
1045 return res;
1046 }
1047 if ( left >= 5 ) { /* >= 5 have the same declension */
1048 snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
1049 } else if ( left >= 2 && left <= 4 ) {
1050 snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
1051 } else { /* left == 1 */
1052 snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
1053 }
1054 num -= left * (exp10_int(length-1));
1055 }
1056 if (!res) {
1057 if (!ast_streamfile(chan, fn, language)) {
1058 if ((audiofd > -1) && (ctrlfd > -1)) {
1059 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1060 } else {
1061 res = ast_waitstream(chan, ints);
1062 }
1063 }
1064 ast_stopstream(chan);
1065 }
1066 }
1067 return res;
1068}
1069
1070/*! \brief ast_say_number_full_da: Danish syntax
1071 New files:
1072 - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
1073 */
1074static 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)
1075{
1076 int res = 0;
1077 int playh = 0;
1078 int playa = 0;
1079 int cn = 1; /* +1 = commune; -1 = neuter */
1080 char fn[256] = "";
1081 if (!num)
1082 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1083
1084 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1085
1086 while (!res && (num || playh || playa )) {
1087 /* The grammar for Danish numbers is the same as for English except
1088 * for the following:
1089 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
1090 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1091 * "one-and twenty" and 68 is "eight-and sixty".
1092 * - "million" is different in singular and plural form
1093 * - numbers > 1000 with zero as the third digit from last have an
1094 * "and" before the last two digits, i.e. 2034 is "two thousand and
1095 * four-and thirty" and 1000012 is "one million and twelve".
1096 */
1097 if (num < 0) {
1098 ast_copy_string(fn, "digits/minus", sizeof(fn));
1099 if ( num > INT_MIN ) {
1100 num = -num;
1101 } else {
1102 num = 0;
1103 }
1104 } else if (playh) {
1105 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1106 playh = 0;
1107 } else if (playa) {
1108 ast_copy_string(fn, "digits/and", sizeof(fn));
1109 playa = 0;
1110 } else if (num == 1 && cn == -1) {
1111 ast_copy_string(fn, "digits/1N", sizeof(fn));
1112 num = 0;
1113 } else if (num < 20) {
1114 snprintf(fn, sizeof(fn), "digits/%d", num);
1115 num = 0;
1116 } else if (num < 100) {
1117 int ones = num % 10;
1118 if (ones) {
1119 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1120 num -= ones;
1121 } else {
1122 snprintf(fn, sizeof(fn), "digits/%d", num);
1123 num = 0;
1124 }
1125 } else {
1126 if (num < 1000) {
1127 int hundreds = num / 100;
1128 if (hundreds == 1)
1129 ast_copy_string(fn, "digits/1N", sizeof(fn));
1130 else
1131 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1132
1133 playh++;
1134 num -= 100 * hundreds;
1135 if (num)
1136 playa++;
1137
1138 } else {
1139 if (num < 1000000) {
1140 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1141 if (res)
1142 return res;
1143 num = num % 1000;
1144 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1145 } else {
1146 if (num < 1000000000) {
1147 int millions = num / 1000000;
1148 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
1149 if (res)
1150 return res;
1151 if (millions == 1)
1152 ast_copy_string(fn, "digits/million", sizeof(fn));
1153 else
1154 ast_copy_string(fn, "digits/millions", sizeof(fn));
1155 num = num % 1000000;
1156 } else {
1157 ast_debug(1, "Number '%d' is too big for me\n", num);
1158 res = -1;
1159 }
1160 }
1161 if (num && num < 100)
1162 playa++;
1163 }
1164 }
1165 if (!res) {
1166 if (!ast_streamfile(chan, fn, language)) {
1167 if ((audiofd > -1) && (ctrlfd > -1))
1168 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1169 else
1170 res = ast_waitstream(chan, ints);
1171 }
1172 ast_stopstream(chan);
1173 }
1174 }
1175 return res;
1176}
1177
1178/*! \brief ast_say_number_full_de: German syntax
1179
1180 New files:
1181 In addition to English, the following sounds are required:
1182 - "millions"
1183 - "1-and" through "9-and"
1184 - "1F" (eine)
1185 - "1N" (ein)
1186 - NB "1" is recorded as 'eins'
1187 */
1188static 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)
1189{
1190 int res = 0, t = 0;
1191 int mf = 1; /* +1 = male and neuter; -1 = female */
1192 char fn[256] = "";
1193 char fna[256] = "";
1194 if (!num)
1195 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1196
1197 if (options && (!strncasecmp(options, "f", 1)))
1198 mf = -1;
1199
1200 while (!res && num) {
1201 /* The grammar for German numbers is the same as for English except
1202 * for the following:
1203 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1204 * "one-and twenty" and 68 is "eight-and sixty".
1205 * - "one" varies according to gender
1206 * - 100 is 'hundert', however all other instances are 'ein hundert'
1207 * - 1000 is 'tausend', however all other instances are 'ein tausend'
1208 * - 1000000 is always 'eine million'
1209 * - "million" is different in singular and plural form
1210 * - 'and' should not go between a hundreds place value and any
1211 * tens/ones place values that follows it. i.e 136 is ein hundert
1212 * sechs und dreizig, not ein hundert und sechs und dreizig.
1213 */
1214 if (num < 0) {
1215 ast_copy_string(fn, "digits/minus", sizeof(fn));
1216 if ( num > INT_MIN ) {
1217 num = -num;
1218 } else {
1219 num = 0;
1220 }
1221 } else if (num == 1 && mf == -1) {
1222 snprintf(fn, sizeof(fn), "digits/%dF", num);
1223 num = 0;
1224 } else if (num < 20) {
1225 snprintf(fn, sizeof(fn), "digits/%d", num);
1226 num = 0;
1227 } else if (num < 100) {
1228 int ones = num % 10;
1229 if (ones) {
1230 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1231 num -= ones;
1232 } else {
1233 snprintf(fn, sizeof(fn), "digits/%d", num);
1234 num = 0;
1235 }
1236 } else if (num == 100 && t == 0) {
1237 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1238 num = 0;
1239 } else if (num < 1000) {
1240 int hundreds = num / 100;
1241 num = num % 100;
1242 if (hundreds == 1) {
1243 ast_copy_string(fn, "digits/1N", sizeof(fn));
1244 } else {
1245 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1246 }
1247 ast_copy_string(fna, "digits/hundred", sizeof(fna));
1248 } else if (num == 1000 && t == 0) {
1249 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1250 num = 0;
1251 } else if (num < 1000000) {
1252 int thousands = num / 1000;
1253 num = num % 1000;
1254 t = 1;
1255 if (thousands == 1) {
1256 ast_copy_string(fn, "digits/1N", sizeof(fn));
1257 ast_copy_string(fna, "digits/thousand", sizeof(fna));
1258 } else {
1259 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
1260 if (res)
1261 return res;
1262 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1263 }
1264 } else if (num < 1000000000) {
1265 int millions = num / 1000000;
1266 num = num % 1000000;
1267 t = 1;
1268 if (millions == 1) {
1269 ast_copy_string(fn, "digits/1F", sizeof(fn));
1270 ast_copy_string(fna, "digits/million", sizeof(fna));
1271 } else {
1272 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
1273 if (res)
1274 return res;
1275 ast_copy_string(fn, "digits/millions", sizeof(fn));
1276 }
1277 } else if (num <= INT_MAX) {
1278 int billions = num / 1000000000;
1279 num = num % 1000000000;
1280 t = 1;
1281 if (billions == 1) {
1282 ast_copy_string(fn, "digits/1F", sizeof(fn));
1283 ast_copy_string(fna, "digits/milliard", sizeof(fna));
1284 } else {
1285 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
1286 if (res) {
1287 return res;
1288 }
1289 ast_copy_string(fn, "digits/milliards", sizeof(fn));
1290 }
1291 } else {
1292 ast_debug(1, "Number '%d' is too big for me\n", num);
1293 res = -1;
1294 }
1295 if (!res) {
1296 if (!ast_streamfile(chan, fn, language)) {
1297 if ((audiofd > -1) && (ctrlfd > -1))
1298 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1299 else
1300 res = ast_waitstream(chan, ints);
1301 }
1302 ast_stopstream(chan);
1303 if (!res) {
1304 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1305 if ((audiofd > -1) && (ctrlfd > -1))
1306 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1307 else
1308 res = ast_waitstream(chan, ints);
1309 }
1310 ast_stopstream(chan);
1311 strcpy(fna, "");
1312 }
1313 }
1314 }
1315 return res;
1316}
1317
1318/*! \brief ast_say_number_full_en_GB: British syntax
1319 New files:
1320 - In addition to American English, the following sounds are required: "vm-and"
1321 */
1322static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1323{
1324 int res = 0;
1325 int playh = 0;
1326 int playa = 0;
1327 char fn[256] = "";
1328 if (!num)
1329 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1330
1331 while (!res && (num || playh || playa )) {
1332 if (num < 0) {
1333 ast_copy_string(fn, "digits/minus", sizeof(fn));
1334 if ( num > INT_MIN ) {
1335 num = -num;
1336 } else {
1337 num = 0;
1338 }
1339 } else if (playh) {
1340 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1341 playh = 0;
1342 } else if (playa) {
1343 ast_copy_string(fn, "vm-and", sizeof(fn));
1344 playa = 0;
1345 } else if (num < 20) {
1346 snprintf(fn, sizeof(fn), "digits/%d", num);
1347 num = 0;
1348 } else if (num < 100) {
1349 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1350 num %= 10;
1351 } else if (num < 1000) {
1352 int hundreds = num / 100;
1353 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1354
1355 playh++;
1356 num -= 100 * hundreds;
1357 if (num)
1358 playa++;
1359 } else if (num < 1000000) {
1360 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
1361 if (res)
1362 return res;
1363 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1364 num %= 1000;
1365 if (num && num < 100)
1366 playa++;
1367 } else if (num < 1000000000) {
1368 int millions = num / 1000000;
1369 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1370 if (res)
1371 return res;
1372 ast_copy_string(fn, "digits/million", sizeof(fn));
1373 num %= 1000000;
1374 if (num && num < 100)
1375 playa++;
1376 } else {
1377 ast_debug(1, "Number '%d' is too big for me\n", num);
1378 res = -1;
1379 }
1380
1381 if (!res) {
1382 if (!ast_streamfile(chan, fn, language)) {
1383 if ((audiofd > -1) && (ctrlfd > -1))
1384 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1385 else
1386 res = ast_waitstream(chan, ints);
1387 }
1388 ast_stopstream(chan);
1389 }
1390 }
1391 return res;
1392}
1393
1394/*! \brief ast_say_number_full_es: Spanish syntax
1395
1396 New files:
1397 Requires a few new audios:
1398 1F.gsm: feminine 'una'
1399 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
1400 */
1401static 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)
1402{
1403 int res = 0;
1404 int playa = 0;
1405 int mf = 0; /* +1 = male; -1 = female */
1406 char fn[256] = "";
1407 if (!num)
1408 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1409
1410 if (options) {
1411 if (!strncasecmp(options, "f", 1))
1412 mf = -1;
1413 else if (!strncasecmp(options, "m", 1))
1414 mf = 1;
1415 }
1416
1417 while (!res && num) {
1418 if (num < 0) {
1419 ast_copy_string(fn, "digits/minus", sizeof(fn));
1420 if ( num > INT_MIN ) {
1421 num = -num;
1422 } else {
1423 num = 0;
1424 }
1425 } else if (playa) {
1426 ast_copy_string(fn, "digits/and", sizeof(fn));
1427 playa = 0;
1428 } else if (num == 1) {
1429 if (mf < 0)
1430 snprintf(fn, sizeof(fn), "digits/%dF", num);
1431 else if (mf > 0)
1432 snprintf(fn, sizeof(fn), "digits/%dM", num);
1433 else
1434 snprintf(fn, sizeof(fn), "digits/%d", num);
1435 num = 0;
1436 } else if (num < 31) {
1437 snprintf(fn, sizeof(fn), "digits/%d", num);
1438 num = 0;
1439 } else if (num < 100) {
1440 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1441 num %= 10;
1442 if (num)
1443 playa++;
1444 } else if (num == 100) {
1445 ast_copy_string(fn, "digits/100", sizeof(fn));
1446 num = 0;
1447 } else if (num < 200) {
1448 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1449 num -= 100;
1450 } else {
1451 if (num < 1000) {
1452 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1453 num %= 100;
1454 } else if (num < 2000) {
1455 num %= 1000;
1456 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1457 } else {
1458 if (num < 1000000) {
1459 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1460 if (res)
1461 return res;
1462 num %= 1000;
1463 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1464 } else {
1465 if (num < 2147483640) {
1466 if ((num/1000000) == 1) {
1467 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1468 if (res)
1469 return res;
1470 ast_copy_string(fn, "digits/million", sizeof(fn));
1471 } else {
1472 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1473 if (res)
1474 return res;
1475 ast_copy_string(fn, "digits/millions", sizeof(fn));
1476 }
1477 num %= 1000000;
1478 } else {
1479 ast_debug(1, "Number '%d' is too big for me\n", num);
1480 res = -1;
1481 }
1482 }
1483 }
1484 }
1485
1486 if (!res) {
1487 if (!ast_streamfile(chan, fn, language)) {
1488 if ((audiofd > -1) && (ctrlfd > -1))
1489 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1490 else
1491 res = ast_waitstream(chan, ints);
1492 }
1493 ast_stopstream(chan);
1494
1495 }
1496
1497 }
1498 return res;
1499}
1500
1501/*! \brief ast_say_number_full_fr: French syntax
1502 Extra sounds needed:
1503 1F: feminin 'une'
1504 et: 'and' */
1505static 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)
1506{
1507 int res = 0;
1508 int playh = 0;
1509 int playa = 0;
1510 int mf = 1; /* +1 = male; -1 = female */
1511 char fn[256] = "";
1512 if (!num)
1513 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1514
1515 if (options && !strncasecmp(options, "f", 1))
1516 mf = -1;
1517
1518 while (!res && (num || playh || playa)) {
1519 if (num < 0) {
1520 ast_copy_string(fn, "digits/minus", sizeof(fn));
1521 if ( num > INT_MIN ) {
1522 num = -num;
1523 } else {
1524 num = 0;
1525 }
1526 } else if (playh) {
1527 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1528 playh = 0;
1529 } else if (playa) {
1530 ast_copy_string(fn, "digits/et", sizeof(fn));
1531 playa = 0;
1532 } else if (num == 1) {
1533 if (mf < 0)
1534 snprintf(fn, sizeof(fn), "digits/%dF", num);
1535 else
1536 snprintf(fn, sizeof(fn), "digits/%d", num);
1537 num = 0;
1538 } else if (num < 21) {
1539 snprintf(fn, sizeof(fn), "digits/%d", num);
1540 num = 0;
1541 } else if (num < 70) {
1542 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1543 if ((num % 10) == 1) playa++;
1544 num = num % 10;
1545 } else if (num < 80) {
1546 ast_copy_string(fn, "digits/60", sizeof(fn));
1547 if ((num % 10) == 1) playa++;
1548 num -= 60;
1549 } else if (num < 100) {
1550 ast_copy_string(fn, "digits/80", sizeof(fn));
1551 num = num - 80;
1552 } else if (num < 200) {
1553 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1554 num = num - 100;
1555 } else if (num < 1000) {
1556 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1557 playh++;
1558 num = num % 100;
1559 } else if (num < 2000) {
1560 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1561 num = num - 1000;
1562 } else if (num < 1000000) {
1563 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1564 if (res)
1565 return res;
1566 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1567 num = num % 1000;
1568 } else if (num < 1000000000) {
1569 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1570 if (res)
1571 return res;
1572 ast_copy_string(fn, "digits/million", sizeof(fn));
1573 num = num % 1000000;
1574 } else {
1575 ast_debug(1, "Number '%d' is too big for me\n", num);
1576 res = -1;
1577 }
1578 if (!res) {
1579 if (!ast_streamfile(chan, fn, language)) {
1580 if ((audiofd > -1) && (ctrlfd > -1))
1581 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1582 else
1583 res = ast_waitstream(chan, ints);
1584 }
1585 ast_stopstream(chan);
1586 }
1587 }
1588 return res;
1589}
1590
1591
1592
1593/* Hebrew syntax
1594 * Check doc/lang/hebrew-digits.txt for information about the various
1595 * recordings required to make this translation work properly */
1596#define SAY_NUM_BUF_SIZE 256
1597static 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)
1598{
1599 int res = 0;
1600 int state = 0; /* no need to save anything */
1601 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1602 int tmpnum = 0;
1603
1604 char fn[SAY_NUM_BUF_SIZE] = "";
1605
1606 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1607
1608 if (!num) {
1609 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1610 }
1611 if (options && !strncasecmp(options, "m", 1)) {
1612 mf = 1;
1613 }
1614 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1615
1616 /* Do we have work to do? */
1617 while (!res && (num || (state > 0))) {
1618 /* first type of work: play a second sound. In this loop
1619 * we can only play one sound file at a time. Thus playing
1620 * a second one requires repeating the loop just for the
1621 * second file. The variable 'state' remembers where we were.
1622 * state==0 is the normal mode and it means that we continue
1623 * to check if the number num has yet anything left.
1624 */
1625 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1626
1627 if (state == 1) {
1628 state = 0;
1629 } else if (state == 2) {
1630 if ((num >= 11) && (num < 21)) {
1631 if (mf < 0) {
1632 snprintf(fn, sizeof(fn), "digits/ve");
1633 } else {
1634 snprintf(fn, sizeof(fn), "digits/uu");
1635 }
1636 } else {
1637 switch (num) {
1638 case 1:
1639 snprintf(fn, sizeof(fn), "digits/ve");
1640 break;
1641 case 2:
1642 snprintf(fn, sizeof(fn), "digits/uu");
1643 break;
1644 case 3:
1645 if (mf < 0) {
1646 snprintf(fn, sizeof(fn), "digits/ve");
1647 } else {
1648 snprintf(fn, sizeof(fn), "digits/uu");
1649 }
1650 break;
1651 case 4:
1652 snprintf(fn, sizeof(fn), "digits/ve");
1653 break;
1654 case 5:
1655 snprintf(fn, sizeof(fn), "digits/ve");
1656 break;
1657 case 6:
1658 snprintf(fn, sizeof(fn), "digits/ve");
1659 break;
1660 case 7:
1661 snprintf(fn, sizeof(fn), "digits/ve");
1662 break;
1663 case 8:
1664 snprintf(fn, sizeof(fn), "digits/uu");
1665 break;
1666 case 9:
1667 snprintf(fn, sizeof(fn), "digits/ve");
1668 break;
1669 case 10:
1670 snprintf(fn, sizeof(fn), "digits/ve");
1671 break;
1672 }
1673 }
1674 state = 0;
1675 } else if (state == 3) {
1676 snprintf(fn, sizeof(fn), "digits/1k");
1677 state = 0;
1678 } else if (num < 0) {
1679 snprintf(fn, sizeof(fn), "digits/minus");
1680 num = (-1) * num;
1681 } else if (num < 20) {
1682 if (mf < 0) {
1683 snprintf(fn, sizeof(fn), "digits/%d", num);
1684 } else {
1685 snprintf(fn, sizeof(fn), "digits/%dm", num);
1686 }
1687 num = 0;
1688 } else if ((num < 100) && (num >= 20)) {
1689 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1690 num = num % 10;
1691 if (num > 0) {
1692 state = 2;
1693 }
1694 } else if ((num >= 100) && (num < 1000)) {
1695 tmpnum = num / 100;
1696 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1697 num = num - (tmpnum * 100);
1698 if ((num > 0) && (num < 11)) {
1699 state = 2;
1700 }
1701 } else if ((num >= 1000) && (num < 10000)) {
1702 tmpnum = num / 1000;
1703 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1704 num = num - (tmpnum * 1000);
1705 if ((num > 0) && (num < 11)) {
1706 state = 2;
1707 }
1708 } else if (num < 20000) {
1709 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1710 num = num % 1000;
1711 state = 3;
1712 } else if (num < 1000000) {
1713 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1714 if (res) {
1715 return res;
1716 }
1717 snprintf(fn, sizeof(fn), "digits/1k");
1718 num = num % 1000;
1719 if ((num > 0) && (num < 11)) {
1720 state = 2;
1721 }
1722 } else if (num < 2000000) {
1723 snprintf(fn, sizeof(fn), "digits/million");
1724 num = num % 1000000;
1725 if ((num > 0) && (num < 11)) {
1726 state = 2;
1727 }
1728 } else if (num < 3000000) {
1729 snprintf(fn, sizeof(fn), "digits/twomillion");
1730 num = num - 2000000;
1731 if ((num > 0) && (num < 11)) {
1732 state = 2;
1733 }
1734 } else if (num < 1000000000) {
1735 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1736 if (res) {
1737 return res;
1738 }
1739 snprintf(fn, sizeof(fn), "digits/million");
1740 num = num % 1000000;
1741 if ((num > 0) && (num < 11)) {
1742 state = 2;
1743 }
1744 } else {
1745 ast_debug(1, "Number '%d' is too big for me\n", num);
1746 res = -1;
1747 }
1748 tmpnum = 0;
1749 if (!res) {
1750 if (!ast_streamfile(chan, fn, language)) {
1751 if ((audiofd > -1) && (ctrlfd > -1)) {
1752 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1753 } else {
1754 res = ast_waitstream(chan, ints);
1755 }
1756 }
1757 ast_stopstream(chan);
1758 }
1759 }
1760 return res;
1761}
1762
1763/*! \brief ast_say_number_full_hu: Hungarian syntax
1764
1765 Extra sounds needed:
1766 10en: "tizen"
1767 20on: "huszon"
1768*/
1769static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1770{
1771 int res = 0;
1772 int playh = 0;
1773 char fn[256] = "";
1774 if (!num)
1775 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1776
1777 /*
1778 Hungarian support
1779 like english, except numbers up to 29 are from 2 words.
1780 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1781 */
1782
1783 while(!res && (num || playh)) {
1784 if (num < 0) {
1785 ast_copy_string(fn, "digits/minus", sizeof(fn));
1786 if ( num > INT_MIN ) {
1787 num = -num;
1788 } else {
1789 num = 0;
1790 }
1791 } else if (playh) {
1792 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1793 playh = 0;
1794 } else if (num < 11 || num == 20) {
1795 snprintf(fn, sizeof(fn), "digits/%d", num);
1796 num = 0;
1797 } else if (num < 20) {
1798 ast_copy_string(fn, "digits/10en", sizeof(fn));
1799 num -= 10;
1800 } else if (num < 30) {
1801 ast_copy_string(fn, "digits/20on", sizeof(fn));
1802 num -= 20;
1803 } else if (num < 100) {
1804 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1805 num %= 10;
1806 } else {
1807 if (num < 1000){
1808 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1809 playh++;
1810 num %= 100;
1811 } else {
1812 if (num < 1000000) { /* 1,000,000 */
1813 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1814 if (res)
1815 return res;
1816 num %= 1000;
1817 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1818 } else {
1819 if (num < 1000000000) { /* 1,000,000,000 */
1820 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1821 if (res)
1822 return res;
1823 num %= 1000000;
1824 ast_copy_string(fn, "digits/million", sizeof(fn));
1825 } else {
1826 ast_debug(1, "Number '%d' is too big for me\n", num);
1827 res = -1;
1828 }
1829 }
1830 }
1831 }
1832 if (!res) {
1833 if(!ast_streamfile(chan, fn, language)) {
1834 if ((audiofd > -1) && (ctrlfd > -1))
1835 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1836 else
1837 res = ast_waitstream(chan, ints);
1838 }
1839 ast_stopstream(chan);
1840 }
1841 }
1842 return res;
1843}
1844
1845/*! \brief ast_say_number_full_is: Icelandic syntax */
1846/* New files:
1847 In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk"
1848 */
1849static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1850{
1851 int res = 0;
1852 int playh = 0;
1853 int playa = 0;
1854 int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */
1855 char fn[256] = "";
1856
1857 if (!num)
1858 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1859
1860 if (options && !strncasecmp(options, "f", 1)) cn = 2;
1861 if (options && !strncasecmp(options, "c", 1)) cn = 3;
1862 /* It seems that sometimes people are using c and sometimes n. */
1863 if (options && !strncasecmp(options, "n", 1)) cn = 3;
1864
1865 while (!res && (num || playh || playa )) {
1866 if (num < 0) {
1867 ast_copy_string(fn, "digits/minus", sizeof(fn));
1868 if ( num > INT_MIN ) {
1869 num = -num;
1870 } else {
1871 num = 0;
1872 }
1873 } else if (playh) {
1874 if (playh > 1)
1875 ast_copy_string(fn, "digits/hundreds", sizeof(fn));
1876 else
1877 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1878 playh = 0;
1879 } else if (playa) {
1880 ast_copy_string(fn, "digits/and", sizeof(fn));
1881 playa = 0;
1882 } else if (num < 5 && cn == 2) {
1883 snprintf(fn, sizeof(fn), "digits/%dkvk", num);
1884 num = 0;
1885 } else if (num < 5 && cn == 3) {
1886 snprintf(fn, sizeof(fn), "digits/%dhk", num);
1887 num = 0;
1888 } else if (num < 20) {
1889 snprintf(fn, sizeof(fn), "digits/%d", num);
1890 num = 0;
1891 } else if (num < 100) {
1892 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1893 num %= 10;
1894 if (num)
1895 playa++;
1896 } else if (num < 1000) {
1897 int hundreds = num / 100;
1898 /* The number prepending hundreds are in neutral */
1899 if (hundreds < 5)
1900 snprintf(fn, sizeof(fn), "digits/%dhk", hundreds);
1901 else
1902 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1903
1904 playh = hundreds;
1905 num -= 100 * hundreds;
1906 if (num && num < 20)
1907 playa++;
1908 /* The 'and' moves forward on even tens. */
1909 if (num && (num % 10) == 0)
1910 playa++;
1911 } else if (num < 1000000) {
1912 res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1913 /* Play 'and' if it's an even hundred. */
1914 if ((num % 100) == 0 && (num % 1000 != 0)) {
1915 playa++;
1916 }
1917 if (res)
1918 return res;
1919 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1920 num %= 1000;
1921 if (num && (num < 20 || (num % 10 == 0)))
1922 playa++;
1923 } else if (num < 1000000000) {
1924 int millions = num / 1000000;
1925 /* The number of millions is feminine */
1926 res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd);
1927 if (res)
1928 return res;
1929 if (millions > 1)
1930 ast_copy_string(fn, "digits/millions", sizeof(fn));
1931 else
1932 ast_copy_string(fn, "digits/million", sizeof(fn));
1933 num %= 1000000;
1934 if (num && num < 100)
1935 playa++;
1936 } else if (num < INT_MAX) {
1937 int milliards = num / 1000000000;
1938 /* The number of milliards is masculine */
1939 res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd);
1940 if (res)
1941 return res;
1942 if (milliards > 1)
1943 ast_copy_string(fn, "digits/milliards", sizeof(fn));
1944 else
1945 ast_copy_string(fn, "digits/milliard", sizeof(fn));
1946 num %= 1000000000;
1947 if (num && num < 100)
1948 playa++;
1949 } else {
1950 ast_debug(1, "Number '%d' is too big for me\n", num);
1951 res = -1;
1952 }
1953
1954 if (!res) {
1955 if (!ast_streamfile(chan, fn, language)) {
1956 if ((audiofd > -1) && (ctrlfd > -1))
1957 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1958 else
1959 res = ast_waitstream(chan, ints);
1960 }
1961 ast_stopstream(chan);
1962 }
1963 }
1964 return res;
1965}
1966
1967
1968/*! \brief ast_say_number_full_it: Italian */
1969static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1970{
1971 int res = 0;
1972 int playh = 0;
1973 int tempnum = 0;
1974 char fn[256] = "";
1975
1976 if (!num)
1977 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1978
1979 /*
1980 Italian support
1981
1982 Like english, numbers up to 20 are a single 'word', and others
1983 compound, but with exceptions.
1984 For example 21 is not twenty-one, but there is a single word in 'it'.
1985 Idem for 28 (ie when a the 2nd part of a compound number
1986 starts with a vowel)
1987
1988 There are exceptions also for hundred, thousand and million.
1989 In english 100 = one hundred, 200 is two hundred.
1990 In italian 100 = cento , like to say hundred (without one),
1991 200 and more are like english.
1992
1993 Same applies for thousand:
1994 1000 is one thousand in en, 2000 is two thousand.
1995 In it we have 1000 = mille , 2000 = 2 mila
1996
1997 For million(s) we use the plural, if more than one
1998 Also, one million is abbreviated in it, like on-million,
1999 or 'un milione', not 'uno milione'.
2000 So the right file is provided.
2001 */
2002
2003 while (!res && (num || playh)) {
2004 if (num < 0) {
2005 ast_copy_string(fn, "digits/minus", sizeof(fn));
2006 if ( num > INT_MIN ) {
2007 num = -num;
2008 } else {
2009 num = 0;
2010 }
2011 } else if (playh) {
2012 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2013 playh = 0;
2014 } else if (num < 20) {
2015 snprintf(fn, sizeof(fn), "digits/%d", num);
2016 num = 0;
2017 } else if (num == 21) {
2018 snprintf(fn, sizeof(fn), "digits/%d", num);
2019 num = 0;
2020 } else if (num == 28) {
2021 snprintf(fn, sizeof(fn), "digits/%d", num);
2022 num = 0;
2023 } else if (num == 31) {
2024 snprintf(fn, sizeof(fn), "digits/%d", num);
2025 num = 0;
2026 } else if (num == 38) {
2027 snprintf(fn, sizeof(fn), "digits/%d", num);
2028 num = 0;
2029 } else if (num == 41) {
2030 snprintf(fn, sizeof(fn), "digits/%d", num);
2031 num = 0;
2032 } else if (num == 48) {
2033 snprintf(fn, sizeof(fn), "digits/%d", num);
2034 num = 0;
2035 } else if (num == 51) {
2036 snprintf(fn, sizeof(fn), "digits/%d", num);
2037 num = 0;
2038 } else if (num == 58) {
2039 snprintf(fn, sizeof(fn), "digits/%d", num);
2040 num = 0;
2041 } else if (num == 61) {
2042 snprintf(fn, sizeof(fn), "digits/%d", num);
2043 num = 0;
2044 } else if (num == 68) {
2045 snprintf(fn, sizeof(fn), "digits/%d", num);
2046 num = 0;
2047 } else if (num == 71) {
2048 snprintf(fn, sizeof(fn), "digits/%d", num);
2049 num = 0;
2050 } else if (num == 78) {
2051 snprintf(fn, sizeof(fn), "digits/%d", num);
2052 num = 0;
2053 } else if (num == 81) {
2054 snprintf(fn, sizeof(fn), "digits/%d", num);
2055 num = 0;
2056 } else if (num == 88) {
2057 snprintf(fn, sizeof(fn), "digits/%d", num);
2058 num = 0;
2059 } else if (num == 91) {
2060 snprintf(fn, sizeof(fn), "digits/%d", num);
2061 num = 0;
2062 } else if (num == 98) {
2063 snprintf(fn, sizeof(fn), "digits/%d", num);
2064 num = 0;
2065 } else if (num < 100) {
2066 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2067 num %= 10;
2068 } else {
2069 if (num < 1000) {
2070 if ((num / 100) > 1) {
2071 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2072 playh++;
2073 } else {
2074 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2075 }
2076 num %= 100;
2077 } else {
2078 if (num < 1000000) { /* 1,000,000 */
2079 if ((num/1000) > 1)
2080 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
2081 if (res)
2082 return res;
2083 tempnum = num;
2084 num %= 1000;
2085 if ((tempnum / 1000) < 2)
2086 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2087 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
2088 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2089 } else {
2090 if (num < 1000000000) { /* 1,000,000,000 */
2091 if ((num / 1000000) > 1)
2092 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2093 if (res)
2094 return res;
2095 tempnum = num;
2096 num %= 1000000;
2097 if ((tempnum / 1000000) < 2)
2098 ast_copy_string(fn, "digits/million", sizeof(fn));
2099 else
2100 ast_copy_string(fn, "digits/millions", sizeof(fn));
2101 } else {
2102 ast_debug(1, "Number '%d' is too big for me\n", num);
2103 res = -1;
2104 }
2105 }
2106 }
2107 }
2108 if (!res) {
2109 if (!ast_streamfile(chan, fn, language)) {
2110 if ((audiofd > -1) && (ctrlfd > -1))
2111 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2112 else
2113 res = ast_waitstream(chan, ints);
2114 }
2115 ast_stopstream(chan);
2116 }
2117 }
2118 return res;
2119}
2120
2121/*! \brief ast_say_number_full_nl: dutch syntax
2122 * New files: digits/nl-en
2123 */
2124static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2125{
2126 int res = 0;
2127 int playh = 0;
2128 int units = 0;
2129 char fn[256] = "";
2130 if (!num)
2131 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2132 while (!res && (num || playh )) {
2133 if (num < 0) {
2134 ast_copy_string(fn, "digits/minus", sizeof(fn));
2135 if ( num > INT_MIN ) {
2136 num = -num;
2137 } else {
2138 num = 0;
2139 }
2140 } else if (playh) {
2141 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2142 playh = 0;
2143 } else if (num < 20) {
2144 snprintf(fn, sizeof(fn), "digits/%d", num);
2145 num = 0;
2146 } else if (num < 100) {
2147 units = num % 10;
2148 if (units > 0) {
2149 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
2150 if (res)
2151 return res;
2152 num = num - units;
2153 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
2154 } else {
2155 snprintf(fn, sizeof(fn), "digits/%d", num - units);
2156 num = 0;
2157 }
2158 } else if (num < 200) {
2159 /* hundred, not one-hundred */
2160 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2161 num %= 100;
2162 } else if (num < 1000) {
2163 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
2164 playh++;
2165 num %= 100;
2166 } else {
2167 if (num < 1100) {
2168 /* thousand, not one-thousand */
2169 num %= 1000;
2170 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2171 } else if (num < 10000) { /* 1,100 to 9,9999 */
2172 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
2173 if (res)
2174 return res;
2175 num %= 100;
2176 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2177 } else {
2178 if (num < 1000000) { /* 1,000,000 */
2179 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
2180 if (res)
2181 return res;
2182 num %= 1000;
2183 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2184 } else {
2185 if (num < 1000000000) { /* 1,000,000,000 */
2186 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2187 if (res)
2188 return res;
2189 num %= 1000000;
2190 ast_copy_string(fn, "digits/million", sizeof(fn));
2191 } else {
2192 ast_debug(1, "Number '%d' is too big for me\n", num);
2193 res = -1;
2194 }
2195 }
2196 }
2197 }
2198
2199 if (!res) {
2200 if (!ast_streamfile(chan, fn, language)) {
2201 if ((audiofd > -1) && (ctrlfd > -1))
2202 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2203 else
2204 res = ast_waitstream(chan, ints);
2205 }
2206 ast_stopstream(chan);
2207 }
2208 }
2209 return res;
2210}
2211
2212/*! \brief ast_say_number_full_no: Norwegian syntax
2213 * New files:
2214 * In addition to American English, the following sounds are required: "and", "1N"
2215 *
2216 * The grammar for Norwegian numbers is the same as for English except
2217 * for the following:
2218 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
2219 * "and" before the last two digits, i.e. 2034 is "two thousand and
2220 * thirty-four" and 1000012 is "one million and twelve".
2221 */
2222static 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)
2223{
2224 int res = 0;
2225 int playh = 0;
2226 int playa = 0;
2227 int cn = 1; /* +1 = commune; -1 = neuter */
2228 char fn[256] = "";
2229
2230 if (!num)
2231 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2232
2233 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2234
2235 while (!res && (num || playh || playa )) {
2236 if (num < 0) {
2237 ast_copy_string(fn, "digits/minus", sizeof(fn));
2238 if ( num > INT_MIN ) {
2239 num = -num;
2240 } else {
2241 num = 0;
2242 }
2243 } else if (playh) {
2244 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2245 playh = 0;
2246 } else if (playa) {
2247 ast_copy_string(fn, "digits/and", sizeof(fn));
2248 playa = 0;
2249 } else if (num == 1 && cn == -1) {
2250 ast_copy_string(fn, "digits/1N", sizeof(fn));
2251 num = 0;
2252 } else if (num < 20) {
2253 snprintf(fn, sizeof(fn), "digits/%d", num);
2254 num = 0;
2255 } else if (num < 100) {
2256 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2257 num %= 10;
2258 } else if (num < 1000) {
2259 int hundreds = num / 100;
2260 if (hundreds == 1)
2261 ast_copy_string(fn, "digits/1N", sizeof(fn));
2262 else
2263 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2264
2265 playh++;
2266 num -= 100 * hundreds;
2267 if (num)
2268 playa++;
2269 } else if (num < 1000000) {
2270 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2271 if (res)
2272 return res;
2273 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2274 num %= 1000;
2275 if (num && num < 100)
2276 playa++;
2277 } else if (num < 1000000000) {
2278 int millions = num / 1000000;
2279 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
2280 if (res)
2281 return res;
2282 ast_copy_string(fn, "digits/million", sizeof(fn));
2283 num %= 1000000;
2284 if (num && num < 100)
2285 playa++;
2286 } else {
2287 ast_debug(1, "Number '%d' is too big for me\n", num);
2288 res = -1;
2289 }
2290
2291 if (!res) {
2292 if (!ast_streamfile(chan, fn, language)) {
2293 if ((audiofd > -1) && (ctrlfd > -1))
2294 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2295 else
2296 res = ast_waitstream(chan, ints);
2297 }
2298 ast_stopstream(chan);
2299 }
2300 }
2301 return res;
2302}
2303
2304typedef struct {
2306 char *cyfry[10];
2307 char *cyfry2[10];
2308 char *setki[10];
2309 char *dziesiatki[10];
2310 char *nastki[10];
2311 char *rzedy[3][3];
2312} odmiana;
2313
2314static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
2315{
2316 if (rzad==0)
2317 return "";
2318
2319 if (i==1)
2320 return odm->rzedy[rzad - 1][0];
2321 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
2322 return odm->rzedy[rzad - 1][1];
2323 else
2324 return odm->rzedy[rzad - 1][2];
2325}
2326
2327static char* pl_append(char* buffer, char* str)
2328{
2329 strcpy(buffer, str);
2330 buffer += strlen(str);
2331 return buffer;
2332}
2333
2334static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
2335{
2336 char file_name[255] = "digits/";
2337 strcat(file_name, fn);
2338 ast_debug(1, "Trying to play: %s\n", file_name);
2339 if (!ast_streamfile(chan, file_name, language)) {
2340 if ((audiofd > -1) && (ctrlfd > -1))
2341 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2342 else
2343 ast_waitstream(chan, ints);
2344 }
2345 ast_stopstream(chan);
2346}
2347
2348static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
2349{
2350 /* Initialise variables to allow compilation on Debian-stable, etc */
2351 int m1000E6 = 0;
2352 int i1000E6 = 0;
2353 int m1000E3 = 0;
2354 int i1000E3 = 0;
2355 int m1000 = 0;
2356 int i1000 = 0;
2357 int m100 = 0;
2358 int i100 = 0;
2359
2360 if (i == 0 && rzad > 0) {
2361 return;
2362 }
2363 if (i == 0) {
2364 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
2365 return;
2366 }
2367
2368 m1000E6 = i % 1000000000;
2369 i1000E6 = i / 1000000000;
2370
2371 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
2372
2373 m1000E3 = m1000E6 % 1000000;
2374 i1000E3 = m1000E6 / 1000000;
2375
2376 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
2377
2378 m1000 = m1000E3 % 1000;
2379 i1000 = m1000E3 / 1000;
2380
2381 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
2382
2383 m100 = m1000 % 100;
2384 i100 = m1000 / 100;
2385
2386 if (i100>0)
2387 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
2388
2389 if (m100 > 0 && m100 <= 9) {
2390 if (m1000 > 0)
2391 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
2392 else
2393 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
2394 } else if (m100 % 10 == 0 && m100 != 0) {
2395 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2396 } else if (m100 > 10 && m100 <= 19) {
2397 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
2398 } else if (m100 > 20) {
2399 if (odm->separator_dziesiatek[0] == ' ') {
2400 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2401 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
2402 } else {
2403 char buf[10];
2404 char *b = buf;
2405 b = pl_append(b, odm->dziesiatki[m100 / 10]);
2407 pl_append(b, odm->cyfry2[m100 % 10]);
2408 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
2409 }
2410 }
2411
2412 if (rzad > 0) {
2413 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
2414 }
2415}
2416
2417/* ast_say_number_full_pl: Polish syntax
2418
2419Sounds needed:
24200 zero
24211 jeden
242210 dziesiec
2423100 sto
24241000 tysiac
24251000000 milion
24261000000000 miliard
24271000000000.2 miliardy
24281000000000.5 miliardow
24291000000.2 miliony
24301000000.5 milionow
24311000.2 tysiace
24321000.5 tysiecy
2433100m stu
243410m dziesieciu
243511 jedenascie
243611m jedenastu
243712 dwanascie
243812m dwunastu
243913 trzynascie
244013m trzynastu
244114 czternascie
244214m czternastu
244315 pietnascie
244415m pietnastu
244516 szesnascie
244616m szesnastu
244717 siedemnascie
244817m siedemnastu
244918 osiemnascie
245018m osiemnastu
245119 dziewietnascie
245219m dziewietnastu
24531z jedna
24542 dwa
245520 dwadziescia
2456200 dwiescie
2457200m dwustu
245820m dwudziestu
24592-1m dwaj
24602-2m dwoch
24612z dwie
24623 trzy
246330 trzydziesci
2464300 trzysta
2465300m trzystu
246630m trzydziestu
24673-1m trzej
24683-2m trzech
24694 cztery
247040 czterdziesci
2471400 czterysta
2472400m czterystu
247340m czterdziestu
24744-1m czterej
24754-2m czterech
24765 piec
247750 piecdziesiat
2478500 piecset
2479500m pieciuset
248050m piedziesieciu
24815m pieciu
24826 szesc
248360 szescdziesiat
2484600 szescset
2485600m szesciuset
248660m szescdziesieciu
24876m szesciu
24887 siedem
248970 siedemdziesiat
2490700 siedemset
2491700m siedmiuset
249270m siedemdziesieciu
24937m siedmiu
24948 osiem
249580 osiemdziesiat
2496800 osiemset
2497800m osmiuset
249880m osiemdziesieciu
24998m osmiu
25009 dziewiec
250190 dziewiecdziesiat
2502900 dziewiecset
2503900m dziewieciuset
250490m dziewiedziesieciu
25059m dziewieciu
2506and combinations of eg.: 20_1, 30m_3m, etc...
2507
2508*/
2509static 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)
2510{
2511 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2512
2513 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2514
2515 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2516
2517 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2518
2519 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2520
2521 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2522
2523 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2524
2525 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2526
2527 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2528
2529 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2530
2531 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2532
2533 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2534
2535 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2536
2537 /* Initialise variables to allow compilation on Debian-stable, etc */
2538 odmiana *o;
2539
2540 static odmiana *odmiana_nieosobowa = NULL;
2541 static odmiana *odmiana_meska = NULL;
2542 static odmiana *odmiana_zenska = NULL;
2543
2544 if (odmiana_nieosobowa == NULL) {
2545 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2546
2547 odmiana_nieosobowa->separator_dziesiatek = " ";
2548
2549 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2550 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2551 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2552 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2553 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2554 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2555 }
2556
2557 if (odmiana_zenska == NULL) {
2558 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2559
2560 odmiana_zenska->separator_dziesiatek = " ";
2561
2562 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2563 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2564 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2565 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2566 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2567 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2568 }
2569
2570 if (odmiana_meska == NULL) {
2571 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2572
2573 odmiana_meska->separator_dziesiatek = " ";
2574
2575 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2576 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2577 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2578 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2579 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2580 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2581 }
2582
2583 if (options) {
2584 if (strncasecmp(options, "f", 1) == 0)
2585 o = odmiana_zenska;
2586 else if (strncasecmp(options, "m", 1) == 0)
2587 o = odmiana_meska;
2588 else
2589 o = odmiana_nieosobowa;
2590 } else
2591 o = odmiana_nieosobowa;
2592
2593 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2594 return 0;
2595}
2596
2597/* ast_say_number_full_pt: Portuguese syntax
2598
2599 * Extra sounds needed:
2600 * For feminin all sound files ends with F
2601 * 100E for 100+ something
2602 * 1000000S for plural
2603 * pt-e for 'and'
2604 */
2605static 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)
2606{
2607 int res = 0;
2608 int playh = 0;
2609 int mf = 1; /* +1 = male; -1 = female */
2610 char fn[256] = "";
2611
2612 if (!num)
2613 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2614
2615 if (options && !strncasecmp(options, "f", 1))
2616 mf = -1;
2617
2618 while (!res && num ) {
2619 if (num < 0) {
2620 ast_copy_string(fn, "digits/minus", sizeof(fn));
2621 if ( num > INT_MIN ) {
2622 num = -num;
2623 } else {
2624 num = 0;
2625 }
2626 } else if (num < 20) {
2627 if ((num == 1 || num == 2) && (mf < 0))
2628 snprintf(fn, sizeof(fn), "digits/%dF", num);
2629 else
2630 snprintf(fn, sizeof(fn), "digits/%d", num);
2631 num = 0;
2632 } else if (num < 100) {
2633 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2634 if (num % 10)
2635 playh = 1;
2636 num = num % 10;
2637 } else if (num < 1000) {
2638 if (num == 100)
2639 ast_copy_string(fn, "digits/100", sizeof(fn));
2640 else if (num < 200)
2641 ast_copy_string(fn, "digits/100E", sizeof(fn));
2642 else {
2643 if (mf < 0 && num > 199)
2644 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2645 else
2646 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2647 if (num % 100)
2648 playh = 1;
2649 }
2650 num = num % 100;
2651 } else if (num < 1000000) {
2652 if (num > 1999) {
2653 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2654 if (res)
2655 return res;
2656 }
2657 ast_copy_string(fn, "digits/1000", sizeof(fn));
2658 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2659 playh = 1;
2660 num = num % 1000;
2661 } else if (num < 1000000000) {
2662 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2663 if (res)
2664 return res;
2665 if (num < 2000000)
2666 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2667 else
2668 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2669
2670 if ((num % 1000000) &&
2671 /* no thousands */
2672 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2673 /* no hundreds and below */
2674 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2675 playh = 1;
2676 num = num % 1000000;
2677 } else {
2678 /* number is too big */
2679 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2680 res = -1;
2681 }
2682 if (!res) {
2683 if (!ast_streamfile(chan, fn, language)) {
2684 if ((audiofd > -1) && (ctrlfd > -1))
2685 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2686 else
2687 res = ast_waitstream(chan, ints);
2688 }
2689 ast_stopstream(chan);
2690 }
2691 if (!res && playh) {
2692 res = wait_file(chan, ints, "digits/pt-e", language);
2693 ast_stopstream(chan);
2694 playh = 0;
2695 }
2696 }
2697 return res;
2698}
2699
2700/*! \brief ast_say_number_full_se: Swedish syntax
2701
2702 Sound files needed
2703 - 1N
2704*/
2705static 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)
2706{
2707 int playh = 0;
2708 int start = 1;
2709 char fn[256] = "";
2710 int cn = 1; /* +1 = commune; -1 = neuter */
2711 int res = 0;
2712
2713 if (!num) {
2714 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2715 }
2716 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2717
2718 while (num || playh) {
2719 if (num < 0) {
2720 ast_copy_string(fn, "digits/minus", sizeof(fn));
2721 if ( num > INT_MIN ) {
2722 num = -num;
2723 } else {
2724 num = 0;
2725 }
2726 } else if (playh) {
2727 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2728 playh = 0;
2729 } else if (start && num < 200 && num > 99 && cn == -1) {
2730 /* Don't say "en hundra" just say "hundra". */
2731 snprintf(fn, sizeof(fn), "digits/hundred");
2732 num -= 100;
2733 } else if (num == 1 && cn == -1) { /* En eller ett? */
2734 ast_copy_string(fn, "digits/1N", sizeof(fn));
2735 num = 0;
2736 } else if (num < 20) {
2737 snprintf(fn, sizeof(fn), "digits/%d", num);
2738 num = 0;
2739 } else if (num < 100) { /* Below hundreds - teens and tens */
2740 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2741 num %= 10;
2742 } else if (num < 1000) {
2743 /* Hundreds */
2744 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2745 playh++;
2746 num %= 100;
2747 } else if (num < 1000000) { /* 1,000,000 */
2748 /* Always say "ett hundra tusen", not "en hundra tusen" */
2749 res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2750 if (res) {
2751 return res;
2752 }
2753 num %= 1000;
2754 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2755 } else if (num < 1000000000) { /* 1,000,000,000 */
2756 /* Always say "en miljon", not "ett miljon" */
2757 res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2758 if (res) {
2759 return res;
2760 }
2761 num %= 1000000;
2762 ast_copy_string(fn, "digits/million", sizeof(fn));
2763 } else { /* Miljarder - Billions */
2764 ast_debug(1, "Number '%d' is too big for me\n", num);
2765 return -1;
2766 }
2767
2768 if (!ast_streamfile(chan, fn, language)) {
2769 if ((audiofd > -1) && (ctrlfd > -1)) {
2770 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2771 } else {
2772 res = ast_waitstream(chan, ints);
2773 }
2774 ast_stopstream(chan);
2775 if (res) {
2776 return res;
2777 }
2778 }
2779 start = 0;
2780 }
2781 return 0;
2782}
2783
2784/*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2785static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2786{
2787 int res = 0;
2788 int playh = 0;
2789 int playt = 0;
2790 int playz = 0;
2791 int last_length = 0;
2792 char buf[20] = "";
2793 char fn[256] = "";
2794 if (!num)
2795 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2796
2797 while (!res && (num || playh || playt || playz)) {
2798 if (num < 0) {
2799 ast_copy_string(fn, "digits/minus", sizeof(fn));
2800 if ( num > INT_MIN ) {
2801 num = -num;
2802 } else {
2803 num = 0;
2804 }
2805 } else if (playz) {
2806 snprintf(fn, sizeof(fn), "digits/0");
2807 last_length = 0;
2808 playz = 0;
2809 } else if (playh) {
2810 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2811 playh = 0;
2812 } else if (playt) {
2813 snprintf(fn, sizeof(fn), "digits/thousand");
2814 playt = 0;
2815 } else if (num < 10) {
2816 snprintf(buf, 12, "%d", num);
2817 if (last_length - strlen(buf) > 1 && last_length != 0) {
2818 last_length = strlen(buf);
2819 playz++;
2820 continue;
2821 }
2822 snprintf(fn, sizeof(fn), "digits/%d", num);
2823 num = 0;
2824 } else if (num < 100) {
2825 snprintf(buf, 10, "%d", num);
2826 if (last_length - strlen(buf) > 1 && last_length != 0) {
2827 last_length = strlen(buf);
2828 playz++;
2829 continue;
2830 }
2831 last_length = strlen(buf);
2832 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2833 num %= 10;
2834 } else {
2835 if (num < 1000){
2836 snprintf(buf, 10, "%d", num);
2837 if (last_length - strlen(buf) > 1 && last_length != 0) {
2838 last_length = strlen(buf);
2839 playz++;
2840 continue;
2841 }
2842 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2843 playh++;
2844 snprintf(buf, 10, "%d", num);
2845 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2846 last_length = strlen(buf);
2847 num -= ((num / 100) * 100);
2848 } else if (num < 10000){
2849 snprintf(buf, 10, "%d", num);
2850 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2851 playt++;
2852 snprintf(buf, 10, "%d", num);
2853 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2854 last_length = strlen(buf);
2855 num -= ((num / 1000) * 1000);
2856 } else if (num < 100000000) { /* 100,000,000 */
2857 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2858 if (res)
2859 return res;
2860 snprintf(buf, 10, "%d", num);
2861 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2862 num -= ((num / 10000) * 10000);
2863 last_length = strlen(buf);
2864 snprintf(fn, sizeof(fn), "digits/wan");
2865 } else {
2866 if (num < 1000000000) { /* 1,000,000,000 */
2867 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2868 if (res)
2869 return res;
2870 snprintf(buf, 10, "%d", num);
2871 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2872 last_length = strlen(buf);
2873 num -= ((num / 100000000) * 100000000);
2874 snprintf(fn, sizeof(fn), "digits/yi");
2875 } else {
2876 ast_debug(1, "Number '%d' is too big for me\n", num);
2877 res = -1;
2878 }
2879 }
2880 }
2881 if (!res) {
2882 if (!ast_streamfile(chan, fn, language)) {
2883 if ((audiofd > -1) && (ctrlfd > -1))
2884 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2885 else
2886 res = ast_waitstream(chan, ints);
2887 }
2888 ast_stopstream(chan);
2889 }
2890 }
2891 return res;
2892}
2893
2894/*!\internal
2895 * \brief Counting in Urdu, the national language of Pakistan
2896 * \since 1.8
2897 */
2898static 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)
2899{
2900 int res = 0;
2901 int playh = 0;
2902 char fn[256] = "";
2903
2904 if (!num) {
2905 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2906 }
2907
2908 while (!res && (num || playh)) {
2909 if (playh) {
2910 snprintf(fn, sizeof(fn), "digits/hundred");
2911 playh = 0;
2912 } else if (num < 100) {
2913 snprintf(fn, sizeof(fn), "digits/%d", num);
2914 num = 0;
2915 } else if (num < 1000) {
2916 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2917 playh++;
2918 num -= ((num / 100) * 100);
2919 } else if (num < 100000) { /* 1,00,000 */
2920 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2921 return res;
2922 }
2923 num = num % 1000;
2924 snprintf(fn, sizeof(fn), "digits/thousand");
2925 } else if (num < 10000000) { /* 1,00,00,000 */
2926 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2927 return res;
2928 }
2929 num = num % 100000;
2930 snprintf(fn, sizeof(fn), "digits/lac");
2931 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2932 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2933 return res;
2934 }
2935 num = num % 10000000;
2936 snprintf(fn, sizeof(fn), "digits/crore");
2937 } else {
2938 ast_debug(1, "Number '%d' is too big for me\n", num);
2939 res = -1;
2940 }
2941
2942 if (!res) {
2943 if (!ast_streamfile(chan, fn, language)) {
2944 if ((audiofd > -1) && (ctrlfd > -1)) {
2945 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2946 } else {
2947 res = ast_waitstream(chan, ints);
2948 }
2949 }
2950 ast_stopstream(chan);
2951 }
2952 }
2953 return res;
2954}
2955
2956/*! \brief determine last digits for thousands/millions (ru) */
2957static int get_lastdigits_ru(int num) {
2958 if (num < 20) {
2959 return num;
2960 } else if (num < 100) {
2961 return get_lastdigits_ru(num % 10);
2962 } else if (num < 1000) {
2963 return get_lastdigits_ru(num % 100);
2964 }
2965 return 0; /* number too big */
2966}
2967
2968
2969/*! \brief ast_say_number_full_ru: Russian syntax
2970
2971 additional files:
2972 n00.gsm (one hundred, two hundred, ...)
2973 thousand.gsm
2974 million.gsm
2975 thousands-i.gsm (tisyachi)
2976 million-a.gsm (milliona)
2977 thousands.gsm
2978 millions.gsm
2979 1f.gsm (odna)
2980 2f.gsm (dve)
2981
2982 where 'n' from 1 to 9
2983*/
2984static 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)
2985{
2986 int res = 0;
2987 int lastdigits = 0;
2988 char fn[256] = "";
2989 if (!num)
2990 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2991
2992 while (!res && (num)) {
2993 if (num < 0) {
2994 ast_copy_string(fn, "digits/minus", sizeof(fn));
2995 if ( num > INT_MIN ) {
2996 num = -num;
2997 } else {
2998 num = 0;
2999 }
3000 } else if (num < 20) {
3001 if (options && strlen(options) == 1 && num < 3) {
3002 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
3003 } else {
3004 snprintf(fn, sizeof(fn), "digits/%d", num);
3005 }
3006 num = 0;
3007 } else if (num < 100) {
3008 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
3009 num %= 10;
3010 } else if (num < 1000){
3011 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
3012 num %= 100;
3013 } else if (num < 1000000) { /* 1,000,000 */
3014 lastdigits = get_lastdigits_ru(num / 1000);
3015 /* say thousands */
3016 if (lastdigits < 3) {
3017 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
3018 } else {
3019 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
3020 }
3021 if (res)
3022 return res;
3023 if (lastdigits == 1) {
3024 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3025 } else if (lastdigits > 1 && lastdigits < 5) {
3026 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
3027 } else {
3028 ast_copy_string(fn, "digits/thousands", sizeof(fn));
3029 }
3030 num %= 1000;
3031 } else if (num < 1000000000) { /* 1,000,000,000 */
3032 lastdigits = get_lastdigits_ru(num / 1000000);
3033 /* say millions */
3034 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
3035 if (res)
3036 return res;
3037 if (lastdigits == 1) {
3038 ast_copy_string(fn, "digits/million", sizeof(fn));
3039 } else if (lastdigits > 1 && lastdigits < 5) {
3040 ast_copy_string(fn, "digits/million-a", sizeof(fn));
3041 } else {
3042 ast_copy_string(fn, "digits/millions", sizeof(fn));
3043 }
3044 num %= 1000000;
3045 } else {
3046 ast_debug(1, "Number '%d' is too big for me\n", num);
3047 res = -1;
3048 }
3049 if (!res) {
3050 if (!ast_streamfile(chan, fn, language)) {
3051 if ((audiofd > -1) && (ctrlfd > -1))
3052 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3053 else
3054 res = ast_waitstream(chan, ints);
3055 }
3056 ast_stopstream(chan);
3057 }
3058 }
3059 return res;
3060}
3061
3062/*! \brief Thai syntax */
3063static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3064{
3065 int res = 0;
3066 int playh = 0;
3067 char fn[256] = "";
3068 if (!num)
3069 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3070
3071 while(!res && (num || playh)) {
3072 if (num < 0) {
3073 ast_copy_string(fn, "digits/lop", sizeof(fn));
3074 if ( num > INT_MIN ) {
3075 num = -num;
3076 } else {
3077 num = 0;
3078 }
3079 } else if (playh) {
3080 ast_copy_string(fn, "digits/roi", sizeof(fn));
3081 playh = 0;
3082 } else if (num < 100) {
3083 if ((num <= 20) || ((num % 10) == 1)) {
3084 snprintf(fn, sizeof(fn), "digits/%d", num);
3085 num = 0;
3086 } else {
3087 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3088 num %= 10;
3089 }
3090 } else if (num < 1000) {
3091 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3092 playh++;
3093 num %= 100;
3094 } else if (num < 10000) { /* 10,000 */
3095 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
3096 if (res)
3097 return res;
3098 num %= 1000;
3099 ast_copy_string(fn, "digits/pan", sizeof(fn));
3100 } else if (num < 100000) { /* 100,000 */
3101 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
3102 if (res)
3103 return res;
3104 num %= 10000;
3105 ast_copy_string(fn, "digits/muan", sizeof(fn));
3106 } else if (num < 1000000) { /* 1,000,000 */
3107 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
3108 if (res)
3109 return res;
3110 num %= 100000;
3111 ast_copy_string(fn, "digits/san", sizeof(fn));
3112 } else {
3113 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3114 if (res)
3115 return res;
3116 num %= 1000000;
3117 ast_copy_string(fn, "digits/larn", sizeof(fn));
3118 }
3119 if (!res) {
3120 if(!ast_streamfile(chan, fn, language)) {
3121 if ((audiofd > -1) && (ctrlfd > -1))
3122 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3123 else
3124 res = ast_waitstream(chan, ints);
3125 }
3126 ast_stopstream(chan);
3127 }
3128 }
3129 return res;
3130}
3131
3132/*! \brief ast_say_number_full_vi: Vietnamese syntax */
3133static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3134{
3135 int res = 0;
3136 int playh = 0;
3137 int playoh = 0;
3138 int playohz = 0;
3139 int playz = 0;
3140 int playl = 0;
3141 char fn[256] = "";
3142 if (!num)
3143 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3144 while (!res && (num || playh)) {
3145 if (num < 0) {
3146 ast_copy_string(fn, "digits/minus", sizeof(fn));
3147 if ( num > INT_MIN ) {
3148 num = -num;
3149 } else {
3150 num = 0;
3151 }
3152 } else if (playl) {
3153 snprintf(fn, sizeof(fn), "digits/%da", num);
3154 playl = 0;
3155 num = 0;
3156 } else if (playh) {
3157 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3158 playh = 0;
3159 } else if (playz) {
3160 ast_copy_string(fn, "digits/odd", sizeof(fn));
3161 playz = 0;
3162 } else if (playoh) {
3163 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
3164 playoh = 0;
3165 } else if (playohz) {
3166 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
3167 playohz = 0;
3168 } else if (num < 20) {
3169 snprintf(fn, sizeof(fn), "digits/%d", num);
3170 num = 0;
3171 } else if (num < 100) {
3172 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
3173 num %= 10;
3174 if ((num == 5) || (num == 4) || (num == 1)) playl++;
3175 } else {
3176 if (num < 1000) {
3177 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3178 num %= 100;
3179 if (num && (num < 10)) {
3180 playz++;
3181 playh++;
3182 } else {
3183 playh++;
3184 }
3185 } else {
3186 if (num < 1000000) { /* 1,000,000 */
3187 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
3188 if (res)
3189 return res;
3190 num %= 1000;
3191 snprintf(fn, sizeof(fn), "digits/thousand");
3192 if (num && (num < 10)) {
3193 playohz++;
3194 } else if (num && (num < 100)){
3195 playoh++;
3196 } else {
3197 playh = 0;
3198 playohz = 0;
3199 playoh = 0;
3200 }
3201 } else {
3202 if (num < 1000000000) { /* 1,000,000,000 */
3203 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3204 if (res)
3205 return res;
3206 num %= 1000000;
3207 ast_copy_string(fn, "digits/million", sizeof(fn));
3208 } else {
3209 res = -1;
3210 }
3211 }
3212 }
3213 }
3214 if (!res) {
3215 if (!ast_streamfile(chan, fn, language)) {
3216 if ((audiofd > -1) && (ctrlfd > -1))
3217 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3218 else
3219 res = ast_waitstream(chan, ints);
3220 }
3221 ast_stopstream(chan);
3222 }
3223 }
3224 return res;
3225}
3226
3227/*! \brief ast_say_enumeration_full: call language-specific functions
3228 * \note Called from AGI */
3229static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3230{
3231 if (!strncasecmp(language, "en", 2)) { /* English syntax */
3232 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3233 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
3234 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
3235 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
3236 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
3237 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
3238 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
3239 } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
3240 return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
3241 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
3242 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3243 }
3244
3245 /* Default to english */
3246 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3247}
3248
3249/*! \brief ast_say_enumeration_full_en: English syntax
3250 \note This is the default syntax, if no other syntax defined in this file is used */
3251static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3252{
3253 int res = 0, t = 0;
3254 char fn[256] = "";
3255
3256 while (!res && num) {
3257 if (num < 0) {
3258 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3259 if ( num > INT_MIN ) {
3260 num = -num;
3261 } else {
3262 num = 0;
3263 }
3264 } else if (num < 20) {
3265 snprintf(fn, sizeof(fn), "digits/h-%d", num);
3266 num = 0;
3267 } else if (num < 100) {
3268 int tens = num / 10;
3269 num = num % 10;
3270 if (num == 0) {
3271 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3272 } else {
3273 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3274 }
3275 } else if (num < 1000) {
3276 int hundreds = num / 100;
3277 num = num % 100;
3278 if (hundreds > 1 || t == 1) {
3279 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3280 }
3281 if (res)
3282 return res;
3283 if (num) {
3284 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3285 } else {
3286 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3287 }
3288 } else if (num < 1000000) {
3289 int thousands = num / 1000;
3290 num = num % 1000;
3291 if (thousands > 1 || t == 1) {
3292 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3293 }
3294 if (res)
3295 return res;
3296 if (num) {
3297 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3298 } else {
3299 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3300 }
3301 t = 1;
3302 } else if (num < 1000000000) {
3303 int millions = num / 1000000;
3304 num = num % 1000000;
3305 t = 1;
3306 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3307 if (res)
3308 return res;
3309 if (num) {
3310 ast_copy_string(fn, "digits/million", sizeof(fn));
3311 } else {
3312 ast_copy_string(fn, "digits/h-million", sizeof(fn));
3313 }
3314 } else if (num < INT_MAX) {
3315 int billions = num / 1000000000;
3316 num = num % 1000000000;
3317 t = 1;
3318 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3319 if (res)
3320 return res;
3321 if (num) {
3322 ast_copy_string(fn, "digits/billion", sizeof(fn));
3323 } else {
3324 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3325 }
3326 } else if (num == INT_MAX) {
3327 ast_copy_string(fn, "digits/h-last", sizeof(fn));
3328 num = 0;
3329 } else {
3330 ast_debug(1, "Number '%d' is too big for me\n", num);
3331 res = -1;
3332 }
3333
3334 if (!res) {
3335 if (!ast_streamfile(chan, fn, language)) {
3336 if ((audiofd > -1) && (ctrlfd > -1)) {
3337 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3338 } else {
3339 res = ast_waitstream(chan, ints);
3340 }
3341 }
3342 ast_stopstream(chan);
3343 }
3344 }
3345 return res;
3346}
3347
3348static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3349{
3350 int res = 0;
3351 char fn[256] = "";
3352 ast_copy_string(fn, "digits/h", sizeof(fn));
3353 if (!res) {
3354 if (!ast_streamfile(chan, fn, language)) {
3355 if ((audiofd > -1) && (ctrlfd > -1)) {
3356 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3357 } else {
3358 res = ast_waitstream(chan, ints);
3359 }
3360 }
3361 ast_stopstream(chan);
3362 }
3363
3364 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3365}
3366
3367/*! \brief ast_say_enumeration_full_da: Danish syntax */
3368static 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)
3369{
3370 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3371 int res = 0, t = 0;
3372 char fn[256] = "", fna[256] = "";
3373 char *gender;
3374
3375 if (options && !strncasecmp(options, "f", 1)) {
3376 gender = "F";
3377 } else if (options && !strncasecmp(options, "n", 1)) {
3378 gender = "N";
3379 } else {
3380 gender = "";
3381 }
3382
3383 if (!num)
3384 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3385
3386 while (!res && num) {
3387 if (num < 0) {
3388 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3389 if ( num > INT_MIN ) {
3390 num = -num;
3391 } else {
3392 num = 0;
3393 }
3394 } else if (num < 100 && t) {
3395 ast_copy_string(fn, "digits/and", sizeof(fn));
3396 t = 0;
3397 } else if (num < 20) {
3398 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3399 num = 0;
3400 } else if (num < 100) {
3401 int ones = num % 10;
3402 if (ones) {
3403 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3404 num -= ones;
3405 } else {
3406 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3407 num = 0;
3408 }
3409 } else if (num == 100 && t == 0) {
3410 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3411 num = 0;
3412 } else if (num < 1000) {
3413 int hundreds = num / 100;
3414 num = num % 100;
3415 if (hundreds == 1) {
3416 ast_copy_string(fn, "digits/1N", sizeof(fn));
3417 } else {
3418 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3419 }
3420 if (num) {
3421 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3422 } else {
3423 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3424 }
3425 t = 1;
3426 } else if (num < 1000000) {
3427 int thousands = num / 1000;
3428 num = num % 1000;
3429 if (thousands == 1) {
3430 if (num) {
3431 ast_copy_string(fn, "digits/1N", sizeof(fn));
3432 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3433 } else {
3434 if (t) {
3435 ast_copy_string(fn, "digits/1N", sizeof(fn));
3436 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3437 } else {
3438 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3439 }
3440 }
3441 } else {
3442 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3443 if (res) {
3444 return res;
3445 }
3446 if (num) {
3447 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3448 } else {
3449 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3450 }
3451 }
3452 t = 1;
3453 } else if (num < 1000000000) {
3454 int millions = num / 1000000;
3455 num = num % 1000000;
3456 if (millions == 1) {
3457 if (num) {
3458 ast_copy_string(fn, "digits/1F", sizeof(fn));
3459 ast_copy_string(fna, "digits/million", sizeof(fna));
3460 } else {
3461 ast_copy_string(fn, "digits/1N", sizeof(fn));
3462 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3463 }
3464 } else {
3465 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3466 if (res) {
3467 return res;
3468 }
3469 if (num) {
3470 ast_copy_string(fn, "digits/millions", sizeof(fn));
3471 } else {
3472 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3473 }
3474 }
3475 t = 1;
3476 } else if (num < INT_MAX) {
3477 int billions = num / 1000000000;
3478 num = num % 1000000000;
3479 if (billions == 1) {
3480 if (num) {
3481 ast_copy_string(fn, "digits/1F", sizeof(fn));
3482 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3483 } else {
3484 ast_copy_string(fn, "digits/1N", sizeof(fn));
3485 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3486 }
3487 } else {
3488 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3489 if (res)
3490 return res;
3491 if (num) {
3492 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3493 } else {
3494 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3495 }
3496 }
3497 t = 1;
3498 } else if (num == INT_MAX) {
3499 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3500 num = 0;
3501 } else {
3502 ast_debug(1, "Number '%d' is too big for me\n", num);
3503 res = -1;
3504 }
3505
3506 if (!res) {
3507 if (!ast_streamfile(chan, fn, language)) {
3508 if ((audiofd > -1) && (ctrlfd > -1))
3509 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3510 else
3511 res = ast_waitstream(chan, ints);
3512 }
3513 ast_stopstream(chan);
3514 if (!res) {
3515 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3516 if ((audiofd > -1) && (ctrlfd > -1)) {
3517 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3518 } else {
3519 res = ast_waitstream(chan, ints);
3520 }
3521 }
3522 ast_stopstream(chan);
3523 strcpy(fna, "");
3524 }
3525 }
3526 }
3527 return res;
3528}
3529
3530/*! \brief ast_say_enumeration_full_de: German syntax */
3531static 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)
3532{
3533 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3534 int res = 0, t = 0;
3535 char fn[256] = "", fna[256] = "";
3536 char *gender;
3537
3538 if (options && !strncasecmp(options, "f", 1)) {
3539 gender = "F";
3540 } else if (options && !strncasecmp(options, "n", 1)) {
3541 gender = "N";
3542 } else {
3543 gender = "";
3544 }
3545
3546 if (!num)
3547 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3548
3549 while (!res && num) {
3550 if (num < 0) {
3551 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3552 if ( num > INT_MIN ) {
3553 num = -num;
3554 } else {
3555 num = 0;
3556 }
3557 } else if (num < 100 && t) {
3558 ast_copy_string(fn, "digits/and", sizeof(fn));
3559 t = 0;
3560 } else if (num < 20) {
3561 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3562 num = 0;
3563 } else if (num < 100) {
3564 int ones = num % 10;
3565 if (ones) {
3566 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3567 num -= ones;
3568 } else {
3569 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3570 num = 0;
3571 }
3572 } else if (num == 100 && t == 0) {
3573 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3574 num = 0;
3575 } else if (num < 1000) {
3576 int hundreds = num / 100;
3577 num = num % 100;
3578 if (hundreds == 1) {
3579 ast_copy_string(fn, "digits/1N", sizeof(fn));
3580 } else {
3581 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3582 }
3583 if (num) {
3584 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3585 } else {
3586 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3587 }
3588 t = 1;
3589 } else if (num < 1000000) {
3590 int thousands = num / 1000;
3591 num = num % 1000;
3592 if (thousands == 1) {
3593 if (num) {
3594 ast_copy_string(fn, "digits/1N", sizeof(fn));
3595 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3596 } else {
3597 if (t) {
3598 ast_copy_string(fn, "digits/1N", sizeof(fn));
3599 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3600 } else {
3601 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3602 }
3603 }
3604 } else {
3605 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3606 if (res) {
3607 return res;
3608 }
3609 if (num) {
3610 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3611 } else {
3612 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3613 }
3614 }
3615 t = 1;
3616 } else if (num < 1000000000) {
3617 int millions = num / 1000000;
3618 num = num % 1000000;
3619 if (millions == 1) {
3620 if (num) {
3621 ast_copy_string(fn, "digits/1F", sizeof(fn));
3622 ast_copy_string(fna, "digits/million", sizeof(fna));
3623 } else {
3624 ast_copy_string(fn, "digits/1N", sizeof(fn));
3625 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3626 }
3627 } else {
3628 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3629 if (res) {
3630 return res;
3631 }
3632 if (num) {
3633 ast_copy_string(fn, "digits/millions", sizeof(fn));
3634 } else {
3635 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3636 }
3637 }
3638 t = 1;
3639 } else if (num < INT_MAX) {
3640 int billions = num / 1000000000;
3641 num = num % 1000000000;
3642 if (billions == 1) {
3643 if (num) {
3644 ast_copy_string(fn, "digits/1F", sizeof(fn));
3645 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3646 } else {
3647 ast_copy_string(fn, "digits/1N", sizeof(fn));
3648 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3649 }
3650 } else {
3651 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3652 if (res)
3653 return res;
3654 if (num) {
3655 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3656 } else {
3657 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3658 }
3659 }
3660 t = 1;
3661 } else if (num == INT_MAX) {
3662 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3663 num = 0;
3664 } else {
3665 ast_debug(1, "Number '%d' is too big for me\n", num);
3666 res = -1;
3667 }
3668
3669 if (!res) {
3670 if (!ast_streamfile(chan, fn, language)) {
3671 if ((audiofd > -1) && (ctrlfd > -1))
3672 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3673 else
3674 res = ast_waitstream(chan, ints);
3675 }
3676 ast_stopstream(chan);
3677 if (!res) {
3678 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3679 if ((audiofd > -1) && (ctrlfd > -1)) {
3680 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3681 } else {
3682 res = ast_waitstream(chan, ints);
3683 }
3684 }
3685 ast_stopstream(chan);
3686 strcpy(fna, "");
3687 }
3688 }
3689 }
3690 return res;
3691}
3692
3693static 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)
3694{
3695 int res = 0;
3696 char fn[256] = "";
3697 int mf = -1; /* +1 = Masculin; -1 = Feminin */
3698 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
3699
3700 if (options && !strncasecmp(options, "m", 1)) {
3701 mf = -1;
3702 }
3703
3704 ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
3705
3706 while (!res && num) {
3707 if (num < 0) {
3708 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
3709 if (num > INT_MIN) {
3710 num = -num;
3711 } else {
3712 num = 0;
3713 }
3714 } else if (num < 21) {
3715 if (mf < 0) {
3716 if (num < 10) {
3717 snprintf(fn, sizeof(fn), "digits/f-0%d", num);
3718 } else {
3719 snprintf(fn, sizeof(fn), "digits/f-%d", num);
3720 }
3721 } else {
3722 if (num < 10) {
3723 snprintf(fn, sizeof(fn), "digits/m-0%d", num);
3724 } else {
3725 snprintf(fn, sizeof(fn), "digits/m-%d", num);
3726 }
3727 }
3728 num = 0;
3729 } else if ((num < 100) && num >= 20) {
3730 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3731 num = num % 10;
3732 } else if ((num >= 100) && (num < 1000)) {
3733 int tmpnum = num / 100;
3734 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
3735 num = num - (tmpnum * 100);
3736 } else if ((num >= 1000) && (num < 10000)) {
3737 int tmpnum = num / 1000;
3738 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
3739 num = num - (tmpnum * 1000);
3740 } else if (num < 20000) {
3741 snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
3742 num = num % 1000;
3743 } else if (num < 1000000) {
3744 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
3745 if (res) {
3746 return res;
3747 }
3748 snprintf(fn, sizeof(fn), "digits/1k");
3749 num = num % 1000;
3750 } else if (num < 2000000) {
3751 snprintf(fn, sizeof(fn), "digits/1m");
3752 num = num % 1000000;
3753 } else if (num < 3000000) {
3754 snprintf(fn, sizeof(fn), "digits/2m");
3755 num = num - 2000000;
3756 } else if (num < 1000000000) {
3757 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
3758 if (res) {
3759 return res;
3760 }
3761 snprintf(fn, sizeof(fn), "digits/1m");
3762 num = num % 1000000;
3763 } else {
3764 ast_debug(1, "Number '%d' is too big for me\n", num);
3765 res = -1;
3766 }
3767 if (!res) {
3768 if (!ast_streamfile(chan, fn, language)) {
3769 if ((audiofd > -1) && (ctrlfd > -1)) {
3770 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3771 } else {
3772 res = ast_waitstream(chan, ints);
3773 }
3774 }
3775 ast_stopstream(chan);
3776 }
3777 }
3778 return res;
3779}
3780
3781/*! \brief ast_say_enumeration_full_is: Icelandic syntax */
3782static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3783{
3784 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3785 int res = 0, t = 0;
3786 char fn[256] = "", fna[256] = "";
3787 char *gender;
3788
3789 if (options && !strncasecmp(options, "f", 1)) {
3790 gender = "F";
3791 } else if (options && !strncasecmp(options, "n", 1)) {
3792 gender = "N";
3793 } else {
3794 gender = "";
3795 }
3796
3797 if (!num)
3798 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3799
3800 while (!res && num) {
3801 if (num < 0) {
3802 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3803 if ( num > INT_MIN ) {
3804 num = -num;
3805 } else {
3806 num = 0;
3807 }
3808 } else if (num < 100 && t) {
3809 ast_copy_string(fn, "digits/and", sizeof(fn));
3810 t = 0;
3811 } else if (num < 20) {
3812 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3813 num = 0;
3814 } else if (num < 100) {
3815 int ones = num % 10;
3816 if (ones) {
3817 int tens = num - ones;
3818 snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender);
3819 num = ones;
3820 t++;
3821 }
3822 else if (t) {
3823 snprintf(fn, sizeof(fn), "digits/and");
3824 t = 0;
3825 }
3826 else {
3827 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3828 num = 0;
3829 }
3830
3831 } else if (num == 100 && t == 0) {
3832 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3833 num = 0;
3834 } else if (num < 1000) {
3835 int hundreds = num / 100;
3836 num = num % 100;
3837 if (hundreds == 1) {
3838 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3839 } else {
3840 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3841 }
3842 if (num) {
3843 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3844 } else {
3845 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3846 }
3847 t = 1;
3848 } else if (num < 1000000) {
3849 int thousands = num / 1000;
3850 num = num % 1000;
3851 if (thousands == 1) {
3852 if (num) {
3853 /* Thousand is a neutral word, so use the neutral recording */
3854 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3855 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3856 } else {
3857 if (t) {
3858 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3859 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3860 } else {
3861 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3862 }
3863 }
3864 } else {
3865 res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd);
3866 if (res) {
3867 return res;
3868 }
3869 if (num) {
3870 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3871 } else {
3872 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3873 }
3874 }
3875 if (num)
3876 t = 1;
3877 } else if (num < 1000000000) {
3878 int millions = num / 1000000;
3879 num = num % 1000000;
3880 if (millions == 1) {
3881 if (num) {
3882 /* Million is a feminine word, so use the female form */
3883 ast_copy_string(fn, "digits/1kvk", sizeof(fn));
3884 ast_copy_string(fna, "digits/million", sizeof(fna));
3885 } else {
3886 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3887 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3888 }
3889 } else {
3890 res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd);
3891 if (res) {
3892 return res;
3893 }
3894 if (num) {
3895 ast_copy_string(fn, "digits/millions", sizeof(fn));
3896 } else {
3897 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3898 }
3899 }
3900 if (num)
3901 t = 1;
3902 } else if (num < INT_MAX) {
3903 int billions = num / 1000000000;
3904 num = num % 1000000000;
3905 if (billions == 1) {
3906 if (num) {
3907 ast_copy_string(fn, "digits/1", sizeof(fn));
3908 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3909 } else {
3910 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3911 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3912 }
3913 } else {
3914 res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd);
3915 if (res)
3916 return res;
3917 if (num) {
3918 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3919 } else {
3920 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3921 }
3922 }
3923 if (num)
3924 t = 1;
3925 } else if (num == INT_MAX) {
3926 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3927 num = 0;
3928 } else {
3929 ast_debug(1, "Number '%d' is too big for me\n", num);
3930 res = -1;
3931 }
3932
3933 if (!res) {
3934 if (!ast_streamfile(chan, fn, language)) {
3935 if ((audiofd > -1) && (ctrlfd > -1))
3936 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3937 else
3938 res = ast_waitstream(chan, ints);
3939 }
3940 ast_stopstream(chan);
3941 if (!res) {
3942 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3943 if ((audiofd > -1) && (ctrlfd > -1)) {
3944 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3945 } else {
3946 res = ast_waitstream(chan, ints);
3947 }
3948 }
3949 ast_stopstream(chan);
3950 strcpy(fna, "");
3951 }
3952 }
3953 }
3954 return res;
3955}
3956
3957static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3958{
3959 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
3960 return ast_say_date_en(chan, t, ints, lang);
3961 } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
3962 return ast_say_date_da(chan, t, ints, lang);
3963 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
3964 return ast_say_date_de(chan, t, ints, lang);
3965 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
3966 return ast_say_date_fr(chan, t, ints, lang);
3967 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
3968 return ast_say_date_gr(chan, t, ints, lang);
3969 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
3970 return ast_say_date_ja(chan, t, ints, lang);
3971 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
3972 return ast_say_date_he(chan, t, ints, lang);
3973 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
3974 return ast_say_date_hu(chan, t, ints, lang);
3975 } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
3976 return ast_say_date_is(chan, t, ints, lang);
3977 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
3978 return ast_say_date_ka(chan, t, ints, lang);
3979 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
3980 return ast_say_date_nl(chan, t, ints, lang);
3981 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
3982 return ast_say_date_pt(chan, t, ints, lang);
3983 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
3984 return ast_say_date_th(chan, t, ints, lang);
3985 }
3986
3987 /* Default to English */
3988 return ast_say_date_en(chan, t, ints, lang);
3989}
3990
3991/*! \brief English syntax */
3992int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3993{
3994 struct ast_tm tm;
3995 struct timeval when = { t, 0 };
3996 char fn[256];
3997 int res = 0;
3998 ast_localtime(&when, &tm, NULL);
3999 if (!res) {
4000 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4001 res = ast_streamfile(chan, fn, lang);
4002 if (!res)
4003 res = ast_waitstream(chan, ints);
4004 }
4005 if (!res) {
4006 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4007 res = ast_streamfile(chan, fn, lang);
4008 if (!res)
4009 res = ast_waitstream(chan, ints);
4010 }
4011 if (!res)
4012 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4013 if (!res)
4014 res = ast_waitstream(chan, ints);
4015 if (!res)
4016 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4017 return res;
4018}
4019
4020/*! \brief Danish syntax */
4021int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4022{
4023 struct timeval when = { t, 0 };
4024 struct ast_tm tm;
4025 char fn[256];
4026 int res = 0;
4027 ast_localtime(&when, &tm, NULL);
4028 if (!res) {
4029 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4030 res = ast_streamfile(chan, fn, lang);
4031 if (!res)
4032 res = ast_waitstream(chan, ints);
4033 }
4034 if (!res)
4035 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4036 if (!res)
4037 res = ast_waitstream(chan, ints);
4038 if (!res) {
4039 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4040 res = ast_streamfile(chan, fn, lang);
4041 if (!res)
4042 res = ast_waitstream(chan, ints);
4043 }
4044 if (!res) {
4045 /* Year */
4046 int year = tm.tm_year + 1900;
4047 if (year > 1999) { /* year 2000 and later */
4048 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4049 } else {
4050 if (year < 1100) {
4051 /* I'm not going to handle 1100 and prior */
4052 /* We'll just be silent on the year, instead of bombing out. */
4053 } else {
4054 /* year 1100 to 1999. will anybody need this?!? */
4055 snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4056 res = wait_file(chan, ints, fn, lang);
4057 if (!res) {
4058 res = wait_file(chan, ints, "digits/hundred", lang);
4059 if (!res && year % 100 != 0) {
4060 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4061 }
4062 }
4063 }
4064 }
4065 }
4066 return res;
4067}
4068
4069/*! \brief German syntax */
4070int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4071{
4072 struct timeval when = { t, 0 };
4073 struct ast_tm tm;
4074 char fn[256];
4075 int res = 0;
4076 ast_localtime(&when, &tm, NULL);
4077 if (!res) {
4078 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4079 res = ast_streamfile(chan, fn, lang);
4080 if (!res)
4081 res = ast_waitstream(chan, ints);
4082 }
4083 if (!res)
4084 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4085 if (!res)
4086 res = ast_waitstream(chan, ints);
4087 if (!res) {
4088 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4089 res = ast_streamfile(chan, fn, lang);
4090 if (!res)
4091 res = ast_waitstream(chan, ints);
4092 }
4093 if (!res) {
4094 /* Year */
4095 int year = tm.tm_year + 1900;
4096 if (year > 1999) { /* year 2000 and later */
4097 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4098 } else {
4099 if (year < 1100) {
4100 /* I'm not going to handle 1100 and prior */
4101 /* We'll just be silent on the year, instead of bombing out. */
4102 } else {
4103 /* year 1100 to 1999. will anybody need this?!? */
4104 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4105 snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
4106 res = wait_file(chan, ints, fn, lang);
4107 if (!res) {
4108 res = wait_file(chan, ints, "digits/hundred", lang);
4109 if (!res && year % 100 != 0) {
4110 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4111 }
4112 }
4113 }
4114 }
4115 }
4116 return res;
4117}
4118
4119/*! \brief Hungarian syntax */
4120int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4121{
4122 struct timeval when = { t, 0 };
4123 struct ast_tm tm;
4124 char fn[256];
4125 int res = 0;
4126 ast_localtime(&when, &tm, NULL);
4127
4128 if (!res)
4129 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4130 if (!res)
4131 res = ast_waitstream(chan, ints);
4132 if (!res) {
4133 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4134 res = ast_streamfile(chan, fn, lang);
4135 if (!res)
4136 res = ast_waitstream(chan, ints);
4137 }
4138 if (!res)
4139 ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
4140 if (!res)
4141 res = ast_waitstream(chan, ints);
4142 if (!res) {
4143 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4144 res = ast_streamfile(chan, fn, lang);
4145 if (!res)
4146 res = ast_waitstream(chan, ints);
4147 }
4148 return res;
4149}
4150
4151/*! \brief French syntax */
4152int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4153{
4154 struct timeval when = { t, 0 };
4155 struct ast_tm tm;
4156 char fn[256];
4157 int res = 0;
4158 ast_localtime(&when, &tm, NULL);
4159 if (!res) {
4160 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4161 res = ast_streamfile(chan, fn, lang);
4162 if (!res)
4163 res = ast_waitstream(chan, ints);
4164 }
4165 if (!res)
4166 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4167 if (!res)
4168 res = ast_waitstream(chan, ints);
4169 if (!res) {
4170 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4171 res = ast_streamfile(chan, fn, lang);
4172 if (!res)
4173 res = ast_waitstream(chan, ints);
4174 }
4175 if (!res)
4176 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4177 return res;
4178}
4179
4180/*! \brief Dutch syntax */
4181int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4182{
4183 struct timeval when = { t, 0 };
4184 struct ast_tm tm;
4185 char fn[256];
4186 int res = 0;
4187 ast_localtime(&when, &tm, NULL);
4188 if (!res) {
4189 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4190 res = ast_streamfile(chan, fn, lang);
4191 if (!res)
4192 res = ast_waitstream(chan, ints);
4193 }
4194 if (!res)
4195 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4196 if (!res) {
4197 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4198 res = ast_streamfile(chan, fn, lang);
4199 if (!res)
4200 res = ast_waitstream(chan, ints);
4201 }
4202 if (!res)
4203 res = ast_waitstream(chan, ints);
4204 if (!res)
4205 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4206 return res;
4207}
4208
4209/*! \brief Thai syntax */
4210int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4211{
4212 struct timeval when = { t, 0 };
4213 struct ast_tm tm;
4214 char fn[256];
4215 int res = 0;
4216 ast_localtime(&when, &tm, NULL);
4217 if (!res) {
4218 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4219 res = ast_streamfile(chan, fn, lang);
4220 ast_copy_string(fn, "digits/tee", sizeof(fn));
4221 res = ast_streamfile(chan, fn, lang);
4222 if (!res)
4223 res = ast_waitstream(chan, ints);
4224 }
4225 if (!res)
4226 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4227 if (!res)
4228 res = ast_waitstream(chan, ints);
4229 if (!res) {
4230 ast_copy_string(fn, "digits/duan", sizeof(fn));
4231 res = ast_streamfile(chan, fn, lang);
4232 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4233 res = ast_streamfile(chan, fn, lang);
4234 if (!res)
4235 res = ast_waitstream(chan, ints);
4236 }
4237 if (!res){
4238 ast_copy_string(fn, "digits/posor", sizeof(fn));
4239 res = ast_streamfile(chan, fn, lang);
4240 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4241 }
4242 return res;
4243}
4244
4245/*! \brief Portuguese syntax */
4246int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4247{
4248 struct timeval when = { t, 0 };
4249 struct ast_tm tm;
4250 char fn[256];
4251 int res = 0;
4252
4253 ast_localtime(&when, &tm, NULL);
4254 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4255 if (!res)
4256 res = wait_file(chan, ints, fn, lang);
4257 if (!res)
4258 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4259 if (!res)
4260 res = wait_file(chan, ints, "digits/pt-de", lang);
4261 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4262 if (!res)
4263 res = wait_file(chan, ints, fn, lang);
4264 if (!res)
4265 res = wait_file(chan, ints, "digits/pt-de", lang);
4266 if (!res)
4267 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4268
4269 return res;
4270}
4271
4272/*! \brief Hebrew syntax */
4273int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4274{
4275 struct timeval when = { t, 0 };
4276 struct ast_tm tm;
4277 char fn[256];
4278 int res = 0;
4279 ast_localtime(&when, &tm, NULL);
4280 if (!res) {
4281 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4282 res = ast_streamfile(chan, fn, lang);
4283 if (!res) {
4284 res = ast_waitstream(chan, ints);
4285 }
4286 }
4287 if (!res) {
4288 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4289 res = ast_streamfile(chan, fn, lang);
4290 if (!res) {
4291 res = ast_waitstream(chan, ints);
4292 }
4293 }
4294 if (!res) {
4295 res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
4296 }
4297 if (!res) {
4298 res = ast_waitstream(chan, ints);
4299 }
4300 if (!res) {
4301 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
4302 }
4303 return res;
4304}
4305
4306/* Icelandic syntax */
4307int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4308{
4309 struct timeval when = { t, 0 };
4310 struct ast_tm tm;
4311 char fn[256];
4312 int res = 0;
4313 ast_localtime(&when, &tm, NULL);
4314 if (!res) {
4315 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4316 res = ast_streamfile(chan, fn, lang);
4317 if (!res)
4318 res = ast_waitstream(chan, ints);
4319 }
4320 if (!res)
4321 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4322 if (!res)
4323 res = ast_waitstream(chan, ints);
4324 if (!res) {
4325 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4326 res = ast_streamfile(chan, fn, lang);
4327 if (!res)
4328 res = ast_waitstream(chan, ints);
4329 }
4330 if (!res) {
4331 /* Year */
4332 int year = tm.tm_year + 1900;
4333 if (year > 1999) { /* year 2000 and later */
4334 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4335 } else {
4336 if (year < 1100) {
4337 /* I'm not going to handle 1100 and prior */
4338 /* We'll just be silent on the year, instead of bombing out. */
4339 } else {
4340 /* year 1100 to 1999. will anybody need this?!? */
4341 snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4342 res = wait_file(chan, ints, fn, lang);
4343 if (!res) {
4344 res = wait_file(chan, ints, "digits/hundred", lang);
4345 if (!res && year % 100 != 0) {
4346 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4347 }
4348 }
4349 }
4350 }
4351 }
4352 return res;
4353}
4354
4355static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4356{
4357 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4358 return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4359 } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4360 return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
4361 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4362 return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
4363 } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
4364 return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
4365 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4366 return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
4367 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4368 return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
4369 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4370 return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
4371 } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4372 return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone);
4373 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4374 return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone);
4375 } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
4376 return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
4377 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4378 return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
4379 } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
4380 return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
4381 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4382 return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
4383 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4384 return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
4385 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
4386 return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
4387 } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
4388 return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
4389 }
4390
4391 /* Default to English */
4392 return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4393}
4394
4395/*! \brief English syntax */
4396int 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)
4397{
4398 struct timeval when = { t, 0 };
4399 struct ast_tm tm;
4400 int res=0, offset, sndoffset;
4401 char sndfile[256], nextmsg[256];
4402
4403 if (format == NULL)
4404 format = "ABdY 'digits/at' IMp";
4405
4406 ast_localtime(&when, &tm, tzone);
4407
4408 for (offset=0 ; format[offset] != '\0' ; offset++) {
4409 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4410 switch (format[offset]) {
4411 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4412 case '\'':
4413 /* Literal name of a sound file */
4414 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4415 sndfile[sndoffset] = format[offset];
4416 }
4417 sndfile[sndoffset] = '\0';
4418 res = wait_file(chan, ints, sndfile, lang);
4419 break;
4420 case 'A':
4421 case 'a':
4422 /* Sunday - Saturday */
4423 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4424 res = wait_file(chan, ints, nextmsg, lang);
4425 break;
4426 case 'B':
4427 case 'b':
4428 case 'h':
4429 /* January - December */
4430 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4431 res = wait_file(chan, ints, nextmsg, lang);
4432 break;
4433 case 'm':
4434 /* Month enumerated */
4435 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
4436 break;
4437 case 'd':
4438 case 'e':
4439 /* First - Thirtyfirst */
4440 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
4441 break;
4442 case 'Y':
4443 /* Year */
4444 if (tm.tm_year > 99) {
4445 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4446 } else if (tm.tm_year < 1) {
4447 /* I'm not going to handle 1900 and prior */
4448 /* We'll just be silent on the year, instead of bombing out. */
4449 } else {
4450 res = wait_file(chan, ints, "digits/19", lang);
4451 if (!res) {
4452 if (tm.tm_year <= 9) {
4453 /* 1901 - 1909 */
4454 res = wait_file(chan, ints, "digits/oh", lang);
4455 }
4456
4457 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
4458 }
4459 }
4460 break;
4461 case 'I':
4462 case 'l':
4463 /* 12-Hour */
4464 if (tm.tm_hour == 0)
4465 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4466 else if (tm.tm_hour > 12)
4467 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4468 else
4469 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4470 res = wait_file(chan, ints, nextmsg, lang);
4471 break;
4472 case 'H':
4473 case 'k':
4474 /* 24-Hour */
4475 if (format[offset] == 'H') {
4476 /* e.g. oh-eight */
4477 if (tm.tm_hour < 10) {
4478 res = wait_file(chan, ints, "digits/oh", lang);
4479 }
4480 } else {
4481 /* e.g. eight */
4482 if (tm.tm_hour == 0) {
4483 res = wait_file(chan, ints, "digits/oh", lang);
4484 }
4485 }
4486 if (!res) {
4487 if (tm.tm_hour != 0) {
4488 int remaining = tm.tm_hour;
4489 if (tm.tm_hour > 20) {
4490 res = wait_file(chan, ints, "digits/20", lang);
4491 remaining -= 20;
4492 }
4493 if (!res) {
4494 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
4495 res = wait_file(chan, ints, nextmsg, lang);
4496 }
4497 }
4498 }
4499 break;
4500 case 'M':
4501 case 'N':
4502 /* Minute */
4503 if (tm.tm_min == 0) {
4504 if (format[offset] == 'M') {
4505 res = wait_file(chan, ints, "digits/oclock", lang);
4506 } else {
4507 res = wait_file(chan, ints, "digits/hundred", lang);
4508 }
4509 } else if (tm.tm_min < 10) {
4510 res = wait_file(chan, ints, "digits/oh", lang);
4511 if (!res) {
4512 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
4513 res = wait_file(chan, ints, nextmsg, lang);
4514 }
4515 } else {
4516 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4517 }
4518 break;
4519 case 'P':
4520 case 'p':
4521 /* AM/PM */
4522 if (tm.tm_hour > 11)
4523 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4524 else
4525 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4526 res = wait_file(chan, ints, nextmsg, lang);
4527 break;
4528 case 'Q':
4529 /* Shorthand for "Today", "Yesterday", or ABdY */
4530 /* XXX As emphasized elsewhere, this should the native way in your
4531 * language to say the date, with changes in what you say, depending
4532 * upon how recent the date is. XXX */
4533 {
4534 struct timeval now = ast_tvnow();
4535 struct ast_tm tmnow;
4536 time_t beg_today;
4537
4538 gettimeofday(&now, NULL);
4539 ast_localtime(&now, &tmnow, tzone);
4540 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4541 /* In any case, it saves not having to do ast_mktime() */
4542 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4543 if (beg_today + 15768000 < t) {
4544 /* More than 6 months from now - "April nineteenth two thousand three" */
4545 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4546 } else if (beg_today + 2628000 < t) {
4547 /* Less than 6 months from now - "August seventh" */
4548 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4549 } else if (beg_today + 86400 * 6 < t) {
4550 /* Less than a month from now - "Sunday, October third" */
4551 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4552 } else if (beg_today + 172800 < t) {
4553 /* Within the next week */
4554 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4555 } else if (beg_today + 86400 < t) {
4556 /* Tomorrow */
4557 res = wait_file(chan, ints, "digits/tomorrow", lang);
4558 } else if (beg_today < t) {
4559 /* Today */
4560 res = wait_file(chan, ints, "digits/today", lang);
4561 } else if (beg_today - 86400 < t) {
4562 /* Yesterday */
4563 res = wait_file(chan, ints, "digits/yesterday", lang);
4564 } else if (beg_today - 86400 * 6 < t) {
4565 /* Within the last week */
4566 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4567 } else if (beg_today - 2628000 < t) {
4568 /* Less than a month ago - "Sunday, October third" */
4569 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4570 } else if (beg_today - 15768000 < t) {
4571 /* Less than 6 months ago - "August seventh" */
4572 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4573 } else {
4574 /* More than 6 months ago - "April nineteenth two thousand three" */
4575 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4576 }
4577 }
4578 break;
4579 case 'q':
4580 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4581 /* XXX As emphasized elsewhere, this should the native way in your
4582 * language to say the date, with changes in what you say, depending
4583 * upon how recent the date is. XXX */
4584 {
4585 struct timeval now;
4586 struct ast_tm tmnow;
4587 time_t beg_today;
4588
4589 now = ast_tvnow();
4590 ast_localtime(&now, &tmnow, tzone);
4591 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4592 /* In any case, it saves not having to do ast_mktime() */
4593 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4594 if (beg_today + 15768000 < t) {
4595 /* More than 6 months from now - "April nineteenth two thousand three" */
4596 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4597 } else if (beg_today + 2628000 < t) {
4598 /* Less than 6 months from now - "August seventh" */
4599 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4600 } else if (beg_today + 86400 * 6 < t) {
4601 /* Less than a month from now - "Sunday, October third" */
4602 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4603 } else if (beg_today + 172800 < t) {
4604 /* Within the next week */
4605 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4606 } else if (beg_today + 86400 < t) {
4607 /* Tomorrow */
4608 res = wait_file(chan, ints, "digits/tomorrow", lang);
4609 } else if (beg_today < t) {
4610 /* Today */
4611 res = wait_file(chan, ints, "digits/today", lang);
4612 } else if (beg_today - 86400 < t) {
4613 /* Yesterday */
4614 res = wait_file(chan, ints, "digits/yesterday", lang);
4615 } else if (beg_today - 86400 * 6 < t) {
4616 /* Within the last week */
4617 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4618 } else if (beg_today - 2628000 < t) {
4619 /* Less than a month ago - "Sunday, October third" */
4620 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4621 } else if (beg_today - 15768000 < t) {
4622 /* Less than 6 months ago - "August seventh" */
4623 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4624 } else {
4625 /* More than 6 months ago - "April nineteenth two thousand three" */
4626 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4627 }
4628 }
4629 break;
4630 case 'R':
4631 res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
4632 break;
4633 case 'S':
4634 /* Seconds */
4635 if (tm.tm_sec == 0) {
4636 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4637 res = wait_file(chan, ints, nextmsg, lang);
4638 } else if (tm.tm_sec < 10) {
4639 res = wait_file(chan, ints, "digits/oh", lang);
4640 if (!res) {
4641 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4642 res = wait_file(chan, ints, nextmsg, lang);
4643 }
4644 } else {
4645 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4646 }
4647 break;
4648 case 'T':
4649 res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
4650 break;
4651 case ' ':
4652 case ' ':
4653 /* Just ignore spaces and tabs */
4654 break;
4655 default:
4656 /* Unknown character */
4657 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4658 }
4659 /* Jump out on DTMF */
4660 if (res) {
4661 break;
4662 }
4663 }
4664 return res;
4665}
4666
4667static char next_item(const char *format)
4668{
4669 const char *next = ast_skip_blanks(format);
4670 return *next;
4671}
4672
4673/*! \brief Danish syntax */
4674int 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)
4675{
4676 struct timeval when = { t, 0 };
4677 struct ast_tm tm;
4678 int res=0, offset, sndoffset;
4679 char sndfile[256], nextmsg[256];
4680
4681 if (!format)
4682 format = "A dBY HMS";
4683
4684 ast_localtime(&when, &tm, tzone);
4685
4686 for (offset=0 ; format[offset] != '\0' ; offset++) {
4687 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4688 switch (format[offset]) {
4689 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4690 case '\'':
4691 /* Literal name of a sound file */
4692 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4693 sndfile[sndoffset] = format[offset];
4694 }
4695 sndfile[sndoffset] = '\0';
4696 res = wait_file(chan, ints, sndfile, lang);
4697 break;
4698 case 'A':
4699 case 'a':
4700 /* Sunday - Saturday */
4701 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4702 res = wait_file(chan, ints, nextmsg, lang);
4703 break;
4704 case 'B':
4705 case 'b':
4706 case 'h':
4707 /* January - December */
4708 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4709 res = wait_file(chan, ints, nextmsg, lang);
4710 break;
4711 case 'm':
4712 /* Month enumerated */
4713 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4714 break;
4715 case 'd':
4716 case 'e':
4717 /* First - Thirtyfirst */
4718 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4719 break;
4720 case 'Y':
4721 /* Year */
4722 {
4723 int year = tm.tm_year + 1900;
4724 if (year > 1999) { /* year 2000 and later */
4725 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4726 } else {
4727 if (year < 1100) {
4728 /* I'm not going to handle 1100 and prior */
4729 /* We'll just be silent on the year, instead of bombing out. */
4730 } else {
4731 /* year 1100 to 1999. will anybody need this?!? */
4732 /* say 1967 as 'nineteen hundred seven and sixty' */
4733 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4734 res = wait_file(chan, ints, nextmsg, lang);
4735 if (!res) {
4736 res = wait_file(chan, ints, "digits/hundred", lang);
4737 if (!res && year % 100 != 0) {
4738 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4739 }
4740 }
4741 }
4742 }
4743 }
4744 break;
4745 case 'I':
4746 case 'l':
4747 /* 12-Hour */
4748 res = wait_file(chan, ints, "digits/oclock", lang);
4749 if (tm.tm_hour == 0)
4750 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4751 else if (tm.tm_hour > 12)
4752 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4753 else
4754 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4755 if (!res) {
4756 res = wait_file(chan, ints, nextmsg, lang);
4757 }
4758 break;
4759 case 'H':
4760 /* 24-Hour, single digit hours preceded by "oh" (0) */
4761 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4762 res = wait_file(chan, ints, "digits/0", lang);
4763 }
4764 /* FALLTRHU */
4765 case 'k':
4766 /* 24-Hour */
4767 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4768 break;
4769 case 'M':
4770 /* Minute */
4771 if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4772 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4773 }
4774 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4775 if (tm.tm_min == 1) {
4776 res = wait_file(chan, ints, "minute", lang);
4777 } else {
4778 res = wait_file(chan, ints, "minutes", lang);
4779 }
4780 }
4781 break;
4782 case 'P':
4783 case 'p':
4784 /* AM/PM */
4785 if (tm.tm_hour > 11)
4786 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4787 else
4788 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4789 res = wait_file(chan, ints, nextmsg, lang);
4790 break;
4791 case 'Q':
4792 /* Shorthand for "Today", "Yesterday", or AdBY */
4793 /* XXX As emphasized elsewhere, this should the native way in your
4794 * language to say the date, with changes in what you say, depending
4795 * upon how recent the date is. XXX */
4796 {
4797 struct timeval now = ast_tvnow();
4798 struct ast_tm tmnow;
4799 time_t beg_today;
4800
4801 ast_localtime(&now, &tmnow, tzone);
4802 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4803 /* In any case, it saves not having to do ast_mktime() */
4804 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4805 if (beg_today < t) {
4806 /* Today */
4807 res = wait_file(chan, ints, "digits/today", lang);
4808 } else if (beg_today - 86400 < t) {
4809 /* Yesterday */
4810 res = wait_file(chan, ints, "digits/yesterday", lang);
4811 } else {
4812 res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4813 }
4814 }
4815 break;
4816 case 'q':
4817 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4818 /* XXX As emphasized elsewhere, this should the native way in your
4819 * language to say the date, with changes in what you say, depending
4820 * upon how recent the date is. XXX */
4821 {
4822 struct timeval now = ast_tvnow();
4823 struct ast_tm tmnow;
4824 time_t beg_today;
4825
4826 ast_localtime(&now, &tmnow, tzone);
4827 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4828 /* In any case, it saves not having to do ast_mktime() */
4829 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4830 if (beg_today < t) {
4831 /* Today */
4832 } else if ((beg_today - 86400) < t) {
4833 /* Yesterday */
4834 res = wait_file(chan, ints, "digits/yesterday", lang);
4835 } else if (beg_today - 86400 * 6 < t) {
4836 /* Within the last week */
4837 res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
4838 } else {
4839 res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4840 }
4841 }
4842 break;
4843 case 'R':
4844 res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
4845 break;
4846 case 'S':
4847 /* Seconds */
4848 res = wait_file(chan, ints, "digits/and", lang);
4849 if (!res) {
4850 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4851 if (!res) {
4852 res = wait_file(chan, ints, "seconds", lang);
4853 }
4854 }
4855 break;
4856 case 'T':
4857 res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
4858 break;
4859 case ' ':
4860 case ' ':
4861 /* Just ignore spaces and tabs */
4862 break;
4863 default:
4864 /* Unknown character */
4865 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4866 }
4867 /* Jump out on DTMF */
4868 if (res) {
4869 break;
4870 }
4871 }
4872 return res;
4873}
4874
4875/*! \brief German syntax */
4876int 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)
4877{
4878 struct timeval when = { t, 0 };
4879 struct ast_tm tm;
4880 int res=0, offset, sndoffset;
4881 char sndfile[256], nextmsg[256];
4882
4883 if (!format)
4884 format = "A dBY HMS";
4885
4886 ast_localtime(&when, &tm, tzone);
4887
4888 for (offset=0 ; format[offset] != '\0' ; offset++) {
4889 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4890 switch (format[offset]) {
4891 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4892 case '\'':
4893 /* Literal name of a sound file */
4894 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4895 sndfile[sndoffset] = format[offset];
4896 }
4897 sndfile[sndoffset] = '\0';
4898 res = wait_file(chan, ints, sndfile, lang);
4899 break;
4900 case 'A':
4901 case 'a':
4902 /* Sunday - Saturday */
4903 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4904 res = wait_file(chan, ints, nextmsg, lang);
4905 break;
4906 case 'B':
4907 case 'b':
4908 case 'h':
4909 /* January - December */
4910 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4911 res = wait_file(chan, ints, nextmsg, lang);
4912 break;
4913 case 'm':
4914 /* Month enumerated */
4915 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4916 break;
4917 case 'd':
4918 case 'e':
4919 /* First - Thirtyfirst */
4920 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4921 break;
4922 case 'Y':
4923 /* Year */
4924 {
4925 int year = tm.tm_year + 1900;
4926 if (year > 1999) { /* year 2000 and later */
4927 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4928 } else {
4929 if (year < 1100) {
4930 /* I'm not going to handle 1100 and prior */
4931 /* We'll just be silent on the year, instead of bombing out. */
4932 } else {
4933 /* year 1100 to 1999. will anybody need this?!? */
4934 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4935 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4936 res = wait_file(chan, ints, nextmsg, lang);
4937 if (!res) {
4938 res = wait_file(chan, ints, "digits/hundred", lang);
4939 if (!res && year % 100 != 0) {
4940 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4941 }
4942 }
4943 }
4944 }
4945 }
4946 break;
4947 case 'I':
4948 case 'l':
4949 /* 12-Hour */
4950 if (tm.tm_hour == 0)
4951 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4952 else if (tm.tm_hour == 1)