Asterisk - The Open Source Telephony Project GIT-master-57e2743
Loading...
Searching...
No Matches
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 const char *find_first_and(const char *lang);
63static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
64
65struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
66 const char *fn;
67 char fnbuf[10], asciibuf[20] = "letters/ascii";
68 char ltr;
69 int num = 0;
70 int res = 0;
71 int upper = 0;
72 int lower = 0;
73
74 struct ast_str *filenames = ast_str_create(20);
75 if (!filenames) {
76 return NULL;
77 }
78 ast_str_reset(filenames);
79
80 while (str[num] && !res) {
81 fn = NULL;
82 switch (str[num]) {
83 case ('*'):
84 fn = "digits/star";
85 break;
86 case ('#'):
87 fn = "digits/pound";
88 break;
89 case ('!'):
90 fn = "letters/exclaimation-point";
91 break;
92 case ('@'):
93 fn = "letters/at";
94 break;
95 case ('$'):
96 fn = "letters/dollar";
97 break;
98 case ('-'):
99 fn = "letters/dash";
100 break;
101 case ('.'):
102 fn = "letters/dot";
103 break;
104 case ('='):
105 fn = "letters/equals";
106 break;
107 case ('+'):
108 fn = "letters/plus";
109 break;
110 case ('/'):
111 fn = "letters/slash";
112 break;
113 case (' '):
114 fn = "letters/space";
115 break;
116 case ('0'):
117 case ('1'):
118 case ('2'):
119 case ('3'):
120 case ('4'):
121 case ('5'):
122 case ('6'):
123 case ('7'):
124 case ('8'):
125 case ('9'):
126 strcpy(fnbuf, "digits/X");
127 fnbuf[7] = str[num];
128 fn = fnbuf;
129 break;
130 default:
131 ltr = str[num];
132 if ('A' <= ltr && ltr <= 'Z') {
133 ltr += 'a' - 'A'; /* file names are all lower-case */
134 switch (sensitivity) {
136 case AST_SAY_CASE_ALL:
137 upper = !upper;
140 break;
141 }
142 } else if ('a' <= ltr && ltr <= 'z') {
143 switch (sensitivity) {
145 case AST_SAY_CASE_ALL:
146 lower = !lower;
149 break;
150 }
151 }
152
153 if (upper) {
154 strcpy(fnbuf, "uppercase");
155 } else if (lower) {
156 strcpy(fnbuf, "lowercase");
157 } else {
158 strcpy(fnbuf, "letters/X");
159 fnbuf[8] = ltr;
160 }
161 fn = fnbuf;
162 }
163 if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
164 (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
165 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
166 }
167 if (upper || lower) {
168 continue;
169 }
170 num++;
171 }
172
173 return filenames;
174}
175
176static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)
177{
178 int res = 0;
179 char *files;
180 const char *fn;
181
182 if (!filenames) {
183 return -1;
184 }
185
186 /* No filenames to play? Return success so we don't hang up erroneously */
187 if (ast_str_strlen(filenames) == 0) {
188 ast_free(filenames);
189 return 0;
190 }
191
192 files = ast_str_buffer(filenames);
193
194 while (!res && (fn = strsep(&files, "&"))) {
195 res = ast_streamfile(chan, fn, lang);
196 if (!res) {
197 if ((audiofd > -1) && (ctrlfd > -1)) {
198 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
199 } else {
200 res = ast_waitstream(chan, ints);
201 }
202 }
203 ast_stopstream(chan);
204 }
205
206 ast_free(filenames);
207
208 return res;
209}
210
211static 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)
212{
213 struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
214 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
215}
216
217struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
218{
219 const char *fn;
220 char fnbuf[256];
221 char ltr;
222 int num = 0;
223
224 struct ast_str *filenames = ast_str_create(20);
225 if (!filenames) {
226 return NULL;
227 }
228 ast_str_reset(filenames);
229
230 while (str[num]) {
231 fn = NULL;
232 switch (str[num]) {
233 case ('*'):
234 fn = "digits/star";
235 break;
236 case ('#'):
237 fn = "digits/pound";
238 break;
239 case ('!'):
240 fn = "letters/exclaimation-point";
241 break;
242 case ('@'):
243 fn = "letters/at";
244 break;
245 case ('$'):
246 fn = "letters/dollar";
247 break;
248 case ('-'):
249 fn = "letters/dash";
250 break;
251 case ('.'):
252 fn = "letters/dot";
253 break;
254 case ('='):
255 fn = "letters/equals";
256 break;
257 case ('+'):
258 fn = "letters/plus";
259 break;
260 case ('/'):
261 fn = "letters/slash";
262 break;
263 case (' '):
264 fn = "letters/space";
265 break;
266 case ('0'):
267 case ('1'):
268 case ('2'):
269 case ('3'):
270 case ('4'):
271 case ('5'):
272 case ('6'):
273 case ('7'):
274 case ('8'):
275 strcpy(fnbuf, "digits/X");
276 fnbuf[7] = str[num];
277 fn = fnbuf;
278 break;
279 default: /* '9' falls here... */
280 ltr = str[num];
281 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
282 strcpy(fnbuf, "phonetic/X_p");
283 fnbuf[9] = ltr;
284 fn = fnbuf;
285 }
286 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
287 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
288 }
289 num++;
290 }
291
292 return filenames;
293}
294
295static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
296{
297 struct ast_str *filenames = ast_get_phonetic_str(str, lang);
298 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
299}
300
301struct ast_str* ast_get_digit_str(const char *str, const char *lang)
302{
303 const char *fn;
304 char fnbuf[256];
305 int num = 0;
306
307 struct ast_str *filenames = ast_str_create(20);
308 if (!filenames) {
309 return NULL;
310 }
311 ast_str_reset(filenames);
312
313 while (str[num]) {
314 fn = NULL;
315 switch (str[num]) {
316 case ('*'):
317 fn = "digits/star";
318 break;
319 case ('#'):
320 fn = "digits/pound";
321 break;
322 case ('-'):
323 fn = "digits/minus";
324 break;
325 case '0':
326 case '1':
327 case '2':
328 case '3':
329 case '4':
330 case '5':
331 case '6':
332 case '7':
333 case '8':
334 case '9':
335 strcpy(fnbuf, "digits/X");
336 fnbuf[7] = str[num];
337 fn = fnbuf;
338 break;
339 }
340 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
341 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
342 }
343 num++;
344 }
345
346 return filenames;
347}
348
349static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
350{
351 struct ast_str *filenames = ast_get_digit_str(str, lang);
352 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
353}
354
355static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
356{
357 const char *fnr;
358 int amt, dollars = 0, cents = 0;
359 struct ast_str *fnrecurse = NULL;
360 struct ast_str *filenames;
361
362 if (ast_strlen_zero(str)) {
363 return NULL;
364 }
365
366 filenames = ast_str_create(20);
367 if (!filenames) {
368 return NULL;
369 }
370 ast_str_reset(filenames);
371
372 /* Don't use %f because floating point rounding
373 * could distort the cents units. Just parse as string. */
374 if (str && *str == '.') {
375 if (sscanf(str, ".%02u", &cents) < 1) {
376 dollars = cents = 0;
377 } else {
378 /* If we have a space instead of numbers after '.',
379 * then it's not quite valid. */
380 const char *period = strchr(str, '.');
381 if (period && !isdigit(*(period + 1))) {
382 cents = 0;
383 }
384 }
385 } else {
386 int res = sscanf(str, "%d.%02u", &dollars, &cents);
387 if (res < 1) {
388 dollars = cents = 0;
389 } else if (res == 2) {
390 const char *period = strchr(str, '.');
391 if (period && !isdigit(*(period + 1))) {
392 cents = 0;
393 }
394 }
395 }
396 amt = dollars * 100 + cents; /* convert everything to cents */
397
398 ast_debug(1, "Amount is %d (%d dollar%s, %d cent%s)\n", amt, dollars, ESS(dollars), cents, ESS(cents));
399
400 if (amt >= 100) {
401 fnrecurse = ast_get_number_str((amt / 100), lang);
402 if (!fnrecurse) {
403 ast_log(LOG_WARNING, "Couldn't get string for dollars\n");
404 } else {
405 fnr = ast_str_buffer(fnrecurse);
406 ast_str_append(&filenames, 0, "%s", fnr);
407 }
408
409 /* If this is it, end on a down pitch, otherwise up pitch */
410 if (amt < 200) {
411 ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
412 } else {
413 ast_str_append(&filenames, 0, "&%s", "dollars");
414 }
415
416 /* If dollars and cents, add "and" in the middle */
417 if (cents > 0) {
418 ast_str_append(&filenames, 0, "&%s", find_first_and(lang));
419 }
420 }
421
422 if (cents > 0) {
423 fnrecurse = ast_get_number_str(cents, lang);
424 if (!fnrecurse) {
425 ast_log(LOG_ERROR, "Couldn't get string for cents\n");
426 } else {
427 fnr = ast_str_buffer(fnrecurse);
428 ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
429 }
430 ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
431 } else if (amt == 0) {
432 fnrecurse = ast_get_digit_str("0", lang);
433 if (!fnrecurse) {
434 ast_log(LOG_ERROR, "Couldn't get string for cents\n");
435 } else {
436 fnr = ast_str_buffer(fnrecurse);
437 ast_str_append(&filenames, 0, "%s", fnr);
438 }
439 ast_str_append(&filenames, 0, "&%s", "cents");
440 }
441
442 if (fnrecurse) {
443 ast_free(fnrecurse);
444 }
445
446 return filenames;
447}
448
449/*! \brief ast_get_money_str: call language-specific functions */
450struct ast_str* ast_get_money_str(const char *str, const char *lang)
451{
452 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
453 return ast_get_money_en_dollars_str(str, lang);
454 }
455
456 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
457 /* Default to english */
458 return ast_get_money_en_dollars_str(str, lang);
459}
460
461static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
462{
463 struct ast_str *filenames = ast_get_money_str(str, lang);
464 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
465}
466
467static struct ast_str* get_number_str_en(int num, const char *lang)
468{
469 const char *fnr;
470 int loops = 0;
471
472 int res = 0;
473 int playh = 0;
474 char fn[256] = "";
475
476 struct ast_str *filenames;
477
478 if (!num) {
479 return ast_get_digit_str("0", lang);
480 }
481
482 filenames = ast_str_create(20);
483 if (!filenames) {
484 return NULL;
485 }
486 ast_str_reset(filenames);
487
488 while (!res && (num || playh)) {
489 if (num < 0) {
490 ast_copy_string(fn, "digits/minus", sizeof(fn));
491 if ( num > INT_MIN ) {
492 num = -num;
493 } else {
494 num = 0;
495 }
496 } else if (playh) {
497 ast_copy_string(fn, "digits/hundred", sizeof(fn));
498 playh = 0;
499 } else if (num < 20) {
500 snprintf(fn, sizeof(fn), "digits/%d", num);
501 num = 0;
502 } else if (num < 100) {
503 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
504 num %= 10;
505 } else {
506 if (num < 1000){
507 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
508 playh++;
509 num %= 100;
510 } else {
511 struct ast_str *fnrecurse = NULL;
512 if (num < 1000000) { /* 1,000,000 */
513 fnrecurse = get_number_str_en((num / 1000), lang);
514 if (!fnrecurse) {
515 ast_log(LOG_ERROR, "Couldn't get string for num\n");
516 } else {
517 fnr = ast_str_buffer(fnrecurse);
518 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
519 }
520 num %= 1000;
521 snprintf(fn, sizeof(fn), "&digits/thousand");
522 } else {
523 if (num < 1000000000) { /* 1,000,000,000 */
524 fnrecurse = get_number_str_en((num / 1000000), lang);
525 if (!fnrecurse) {
526 ast_log(LOG_ERROR, "Couldn't get string for num\n");
527 } else {
528 fnr = ast_str_buffer(fnrecurse);
529 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
530 }
531 num %= 1000000;
532 ast_copy_string(fn, "&digits/million", sizeof(fn));
533 } else {
534 if (num < INT_MAX) {
535 fnrecurse = get_number_str_en((num / 1000000000), lang);
536 if (!fnrecurse) {
537 ast_log(LOG_ERROR, "Couldn't get string for num\n");
538 } else {
539 fnr = ast_str_buffer(fnrecurse);
540 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
541 }
542 num %= 1000000000;
543 ast_copy_string(fn, "&digits/billion", sizeof(fn));
544 } else {
545 ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
546 res = -1;
547 }
548 }
549 }
550 if (fnrecurse) {
551 ast_free(fnrecurse);
552 }
553 /* we already decided whether or not to add an &, don't add another one immediately */
554 loops = 0;
555 }
556 }
557 if (!res) {
558 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
559 loops++;
560 }
561 }
562
563 return filenames;
564}
565
566/*! \brief ast_get_number_str: call language-specific functions */
567struct ast_str* ast_get_number_str(int num, const char *lang)
568{
569 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
570 return get_number_str_en(num, lang);
571 }
572
573 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
574 /* Default to english */
575 return get_number_str_en(num, lang);
576}
577
578static struct ast_str* get_ordinal_str_en(int num, const char *lang)
579{
580 const char *fnr;
581 int loops = 0;
582
583 int res = 0;
584 int playh = 0;
585 char fn[256] = "";
586
587 struct ast_str *filenames;
588
589 if (!num) {
590 num = 0;
591 }
592
593 filenames = ast_str_create(20);
594 if (!filenames) {
595 return NULL;
596 }
597 ast_str_reset(filenames);
598
599 while (!res && (num || playh)) {
600 if (num < 0) {
601 ast_copy_string(fn, "digits/minus", sizeof(fn));
602 if ( num > INT_MIN ) {
603 num = -num;
604 } else {
605 num = 0;
606 }
607 } else if (playh) {
608 ast_copy_string(fn, (num % 100 == 0) ? "digits/h-hundred" : "digits/hundred", sizeof(fn));
609 playh = 0;
610 } else if (num < 20) {
611 if (num > 0) {
612 snprintf(fn, sizeof(fn), "digits/h-%d", num);
613 } else {
614 ast_log(LOG_ERROR, "Unsupported ordinal number: %d\n", num);
615 }
616 num = 0;
617 } else if (num < 100) {
618 int base = (num / 10) * 10;
619 if (base != num) {
620 snprintf(fn, sizeof(fn), "digits/%d", base);
621 } else {
622 snprintf(fn, sizeof(fn), "digits/h-%d", base);
623 }
624 num %= 10;
625 } else {
626 if (num < 1000){
627 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
628 playh++;
629 num %= 100;
630 } else {
631 struct ast_str *fnrecurse = NULL;
632 if (num < 1000000) { /* 1,000,000 */
633 fnrecurse = get_number_str_en((num / 1000), lang);
634 if (!fnrecurse) {
635 ast_log(LOG_ERROR, "Couldn't get string for num\n");
636 } else {
637 fnr = ast_str_buffer(fnrecurse);
638 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
639 }
640 num %= 1000;
641 snprintf(fn, sizeof(fn), (num % 1000 == 0) ? "&digits/h-thousand" : "&digits/thousand");
642 } else {
643 if (num < 1000000000) { /* 1,000,000,000 */
644 fnrecurse = get_number_str_en((num / 1000000), lang);
645 if (!fnrecurse) {
646 ast_log(LOG_ERROR, "Couldn't get string for num\n");
647 } else {
648 fnr = ast_str_buffer(fnrecurse);
649 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
650 }
651 num %= 1000000;
652 ast_copy_string(fn, (num % 1000000 == 0) ? "&digits/h-million" : "&digits/million", sizeof(fn));
653 } else {
654 if (num < INT_MAX) {
655 fnrecurse = get_number_str_en((num / 1000000000), lang);
656 if (!fnrecurse) {
657 ast_log(LOG_ERROR, "Couldn't get string for num\n");
658 } else {
659 fnr = ast_str_buffer(fnrecurse);
660 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
661 }
662 num %= 1000000000;
663 ast_copy_string(fn, (num % 1000000000 == 0) ? "&digits/h-billion" : "&digits/billion", sizeof(fn));
664 } else {
665 ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
666 res = -1;
667 }
668 }
669 }
670 if (fnrecurse) {
671 ast_free(fnrecurse);
672 }
673 /* we already decided whether or not to add an &, don't add another one immediately */
674 loops = 0;
675 }
676 }
677 if (!res) {
678 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
679 loops++;
680 }
681 }
682
683 return filenames;
684}
685
686/*! \brief ast_get_ordinal_str: call language-specific functions */
687struct ast_str* ast_get_ordinal_str(int num, const char *lang)
688{
689 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
690 return get_ordinal_str_en(num, lang);
691 }
692
693 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
694 /* Default to english */
695 return get_ordinal_str_en(num, lang);
696}
697
698/* Forward declarations */
699/*! \page Def_syntaxlang Asterisk Language Syntaxes supported
700 \note Not really language codes.
701 For these language codes, Asterisk will change the syntax when
702 saying numbers (and in some cases dates and voicemail messages
703 as well)
704 \arg \b da - Danish
705 \arg \b de - German
706 \arg \b en - English (US)
707 \arg \b en_GB - English (British)
708 \arg \b es - Spanish, Mexican
709 \arg \b fr - French
710 \arg \b he - Hebrew
711 \arg \b is - Icelandic
712 \arg \b it - Italian
713 \arg \b nl - Dutch
714 \arg \b no - Norwegian
715 \arg \b pl - Polish
716 \arg \b pt - Portuguese
717 \arg \b pt_BR - Portuguese (Brazil)
718 \arg \b se - Swedish
719 \arg \b zh - Taiwanese / Chinese
720 \arg \b ru - Russian
721 \arg \b ka - Georgian
722 \arg \b hu - Hungarian
723
724 \par Gender:
725 For some languages the numbers differ for gender of the countable object.
726 Commonly for "one", like "un"/"une" in French. Note that the interface
727 is somewhat peculiar, as differing languages can have conflicting
728 genders.
729 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
730 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
731
732 Date/Time functions currently have less languages supported than saynumber().
733
734 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
735
736 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
737
738 \par Portuguese
739 Portuguese sound files needed for Time/Date functions:
740 pt-ah
741 pt-ao
742 pt-de
743 pt-e
744 pt-ora
745 pt-meianoite
746 pt-meiodia
747 pt-sss
748
749 \par Spanish
750 Spanish sound files needed for Time/Date functions:
751 es-de
752 es-el
753
754 \par Italian
755 Italian sound files needed for Time/Date functions:
756 ore-una
757 ore-mezzanotte
758
759*/
760
761/* Forward declarations of language specific variants of ast_say_number_full */
762static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
763static 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);
764static 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);
765static 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);
766static int ast_say_number_full_ps(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_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
768static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
769static 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);
770static 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);
771static 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);
772static 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);
773static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
774static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
775static 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);
776static 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);
777static 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);
778static 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);
779static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
780static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
781static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
782static 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);
783static 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);
784static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
785static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
786static 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);
787static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
788
789/* Forward declarations of language specific variants of ast_say_enumeration_full */
790static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
791static 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);
792static 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);
793static 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);
794static 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);
795static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
796static int ast_say_enumeration_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
797static int ast_say_enumeration_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
798
799/* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
800static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
801static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
802static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
803static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
804static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
805static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
806static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
807static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
808static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
809static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
810static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
811static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
812static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
813
814static 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);
815static 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);
816static 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);
817static 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);
818static 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);
819static 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);
820static 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);
821static 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);
822static 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);
823static 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);
824static 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);
825static 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);
826static 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);
827static 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);
828static 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);
829static 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);
830
831static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
832static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
833static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
834static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
835static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
836static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
837static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
838static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
839static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
840static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
841static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
842static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
843static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
844
845static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
846static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
847static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
848static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
849static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
850static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
851static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
852static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
853static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
854static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
855static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
856static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
857static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
858
859static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
860static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
861static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
862static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
863static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
864
865static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
866{
867 int res;
868 if ((res = ast_streamfile(chan, file, lang))) {
869 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
870 }
871 if (!res) {
872 res = ast_waitstream(chan, ints);
873 }
874 return res;
875}
876
877static const char *find_first_and(const char *language)
878{
879 if (ast_fileexists("digits/and", NULL, language)) {
880 return "digits/and";
881 } else if (ast_fileexists("and", NULL, language)) {
882 return "and";
883 } else {
884 return "vm-and";
885 }
886}
887
888/*! \brief ast_say_number_full: call language-specific functions
889 \note Called from AGI */
890static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
891{
892 ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
893 if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
894 return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
895 } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
896 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
897 } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
898 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
899 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
900 return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
901 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
902 return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
903 } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
904 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
905 } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
906 return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
907 } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
908 return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
909 } else if (!strncasecmp(language, "ja", 2)) { /* Japanese syntax */
910 return ast_say_number_full_ja(chan, num, ints, language, audiofd, ctrlfd);
911 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
912 return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
913 } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
914 return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
915 } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
916 return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
917 } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
918 return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
919 } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
920 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
921 } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
922 return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
923 } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
924 return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
925 } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
926 return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
927 } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
928 return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
929 } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
930 return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
931 } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
932 return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
933 } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
934 return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
935 } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
936 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
937 } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
938 return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
939 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
940 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
941 } else if (!strncasecmp(language, "dr", 2)) { /* Dari syntax */
942 return ast_say_number_full_dr(chan, num, ints, language, audiofd, ctrlfd);
943 } else if (!strncasecmp(language, "ps", 2)) { /* Pashto syntax */
944 return ast_say_number_full_ps(chan, num, ints, language, options, audiofd, ctrlfd);
945 }
946
947 /* Default to english */
948 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
949}
950
951/*! \brief ast_say_number_full_en: English syntax
952 \note This is the default syntax, if no other syntax defined in this file is used */
953static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
954{
955 struct ast_str *filenames = ast_get_number_str(num, language);
956 return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
957}
958
959/*! \brief say_ordinal_full */
960static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
961{
962 struct ast_str *filenames = ast_get_ordinal_str(num, language);
963 return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
964}
965
966static int exp10_int(int power)
967{
968 int x, res= 1;
969 for (x=0;x<power;x++)
970 res *= 10;
971 return res;
972}
973
974/*! \brief ast_say_number_full_cs: Czech syntax
975 *
976 * files needed:
977 * - 1m,2m - gender male
978 * - 1w,2w - gender female
979 * - 3,4,...,20
980 * - 30,40,...,90
981 *
982 * - hundreds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
983 *
984 * for each number 10^(3n + 3) exist 3 files represented as:
985 * 1 thousand = jeden tisic = 1_E3
986 * 2,3,4 thousands = dva,tri,ctyri tisice = 2-3_E3
987 * 5,6,... thousands = pet,sest,... tisic = 5_E3
988 *
989 * million = _E6
990 * miliard = _E9
991 * etc...
992 *
993 * thousand, milion are gender male, so 1 and 2 is 1m 2m
994 * miliard is gender female, so 1 and 2 is 1w 2w
995 */
996static 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)
997{
998 int res = 0;
999 int playh = 0;
1000 char fn[256] = "";
1001
1002 int hundred = 0;
1003 int left = 0;
1004 int length = 0;
1005
1006 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
1007 if (!options)
1008 options = "w";
1009
1010 if (!num)
1011 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1012
1013 while (!res && (num || playh)) {
1014 if (num < 0) {
1015 ast_copy_string(fn, "digits/minus", sizeof(fn));
1016 if ( num > INT_MIN ) {
1017 num = -num;
1018 } else {
1019 num = 0;
1020 }
1021 } else if (num < 3 ) {
1022 snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
1023 playh = 0;
1024 num = 0;
1025 } else if (num < 20) {
1026 snprintf(fn, sizeof(fn), "digits/%d", num);
1027 playh = 0;
1028 num = 0;
1029 } else if (num < 100) {
1030 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1031 num %= 10;
1032 } else if (num < 1000) {
1033 hundred = num / 100;
1034 if ( hundred == 1 ) {
1035 ast_copy_string(fn, "digits/1sto", sizeof(fn));
1036 } else if ( hundred == 2 ) {
1037 ast_copy_string(fn, "digits/2ste", sizeof(fn));
1038 } else {
1039 res = ast_say_number_full_cs(chan, hundred, ints, language, options, audiofd, ctrlfd);
1040 if (res)
1041 return res;
1042 if (hundred == 3 || hundred == 4) {
1043 ast_copy_string(fn, "digits/sta", sizeof(fn));
1044 } else if ( hundred > 4 ) {
1045 ast_copy_string(fn, "digits/set", sizeof(fn));
1046 }
1047 }
1048 num -= (hundred * 100);
1049 } else { /* num > 1000 */
1050 length = (int)log10(num)+1;
1051 while ( (length % 3 ) != 1 ) {
1052 length--;
1053 }
1054 left = num / (exp10_int(length-1));
1055 if ( left == 2 ) {
1056 switch (length-1) {
1057 case 9: options = "w"; /* 1,000,000,000 gender female */
1058 break;
1059 default : options = "m"; /* others are male */
1060 }
1061 }
1062 if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
1063 res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
1064 if (res)
1065 return res;
1066 }
1067 if ( left >= 5 ) { /* >= 5 have the same declension */
1068 snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
1069 } else if ( left >= 2 && left <= 4 ) {
1070 snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
1071 } else { /* left == 1 */
1072 snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
1073 }
1074 num -= left * (exp10_int(length-1));
1075 }
1076 if (!res) {
1077 if (!ast_streamfile(chan, fn, language)) {
1078 if ((audiofd > -1) && (ctrlfd > -1)) {
1079 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1080 } else {
1081 res = ast_waitstream(chan, ints);
1082 }
1083 }
1084 ast_stopstream(chan);
1085 }
1086 }
1087 return res;
1088}
1089
1090/*! \brief ast_say_number_full_da: Danish syntax
1091 New files:
1092 - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
1093 */
1094static 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)
1095{
1096 int res = 0;
1097 int playh = 0;
1098 int playa = 0;
1099 int cn = 1; /* +1 = commune; -1 = neuter */
1100 char fn[256] = "";
1101 if (!num)
1102 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1103
1104 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1105
1106 while (!res && (num || playh || playa )) {
1107 /* The grammar for Danish numbers is the same as for English except
1108 * for the following:
1109 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
1110 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1111 * "one-and twenty" and 68 is "eight-and sixty".
1112 * - "million" is different in singular and plural form
1113 * - numbers > 1000 with zero as the third digit from last have an
1114 * "and" before the last two digits, i.e. 2034 is "two thousand and
1115 * four-and thirty" and 1000012 is "one million and twelve".
1116 */
1117 if (num < 0) {
1118 ast_copy_string(fn, "digits/minus", sizeof(fn));
1119 if ( num > INT_MIN ) {
1120 num = -num;
1121 } else {
1122 num = 0;
1123 }
1124 } else if (playh) {
1125 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1126 playh = 0;
1127 } else if (playa) {
1128 ast_copy_string(fn, find_first_and(language), sizeof(fn));
1129 playa = 0;
1130 } else if (num == 1 && cn == -1) {
1131 ast_copy_string(fn, "digits/1N", sizeof(fn));
1132 num = 0;
1133 } else if (num < 20) {
1134 snprintf(fn, sizeof(fn), "digits/%d", num);
1135 num = 0;
1136 } else if (num < 100) {
1137 int ones = num % 10;
1138 if (ones) {
1139 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1140 num -= ones;
1141 } else {
1142 snprintf(fn, sizeof(fn), "digits/%d", num);
1143 num = 0;
1144 }
1145 } else {
1146 if (num < 1000) {
1147 int hundreds = num / 100;
1148 if (hundreds == 1)
1149 ast_copy_string(fn, "digits/1N", sizeof(fn));
1150 else
1151 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1152
1153 playh++;
1154 num -= 100 * hundreds;
1155 if (num)
1156 playa++;
1157
1158 } else {
1159 if (num < 1000000) {
1160 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1161 if (res)
1162 return res;
1163 num = num % 1000;
1164 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1165 } else {
1166 if (num < 1000000000) {
1167 int millions = num / 1000000;
1168 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
1169 if (res)
1170 return res;
1171 if (millions == 1)
1172 ast_copy_string(fn, "digits/million", sizeof(fn));
1173 else
1174 ast_copy_string(fn, "digits/millions", sizeof(fn));
1175 num = num % 1000000;
1176 } else {
1177 ast_debug(1, "Number '%d' is too big for me\n", num);
1178 res = -1;
1179 }
1180 }
1181 if (num && num < 100)
1182 playa++;
1183 }
1184 }
1185 if (!res) {
1186 if (!ast_streamfile(chan, fn, language)) {
1187 if ((audiofd > -1) && (ctrlfd > -1))
1188 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1189 else
1190 res = ast_waitstream(chan, ints);
1191 }
1192 ast_stopstream(chan);
1193 }
1194 }
1195 return res;
1196}
1197
1198/*! \brief ast_say_number_full_de: German syntax
1199
1200 New files:
1201 In addition to English, the following sounds are required:
1202 - "millions"
1203 - "1-and" through "9-and"
1204 - "1F" (eine)
1205 - "1N" (ein)
1206 - NB "1" is recorded as 'eins'
1207 */
1208static 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)
1209{
1210 int res = 0, t = 0;
1211 int mf = 1; /* +1 = male and neuter; -1 = female */
1212 char fn[256] = "";
1213 char fna[256] = "";
1214 if (!num)
1215 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1216
1217 if (options && (!strncasecmp(options, "f", 1)))
1218 mf = -1;
1219
1220 while (!res && num) {
1221 /* The grammar for German numbers is the same as for English except
1222 * for the following:
1223 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1224 * "one-and twenty" and 68 is "eight-and sixty".
1225 * - "one" varies according to gender
1226 * - 100 is 'hundert', however all other instances are 'ein hundert'
1227 * - 1000 is 'tausend', however all other instances are 'ein tausend'
1228 * - 1000000 is always 'eine million'
1229 * - "million" is different in singular and plural form
1230 * - 'and' should not go between a hundreds place value and any
1231 * tens/ones place values that follows it. i.e 136 is ein hundert
1232 * sechs und dreizig, not ein hundert und sechs und dreizig.
1233 */
1234 if (num < 0) {
1235 ast_copy_string(fn, "digits/minus", sizeof(fn));
1236 if ( num > INT_MIN ) {
1237 num = -num;
1238 } else {
1239 num = 0;
1240 }
1241 } else if (num == 1 && mf == -1) {
1242 snprintf(fn, sizeof(fn), "digits/%dF", num);
1243 num = 0;
1244 } else if (num < 20) {
1245 snprintf(fn, sizeof(fn), "digits/%d", num);
1246 num = 0;
1247 } else if (num < 100) {
1248 int ones = num % 10;
1249 if (ones) {
1250 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1251 num -= ones;
1252 } else {
1253 snprintf(fn, sizeof(fn), "digits/%d", num);
1254 num = 0;
1255 }
1256 } else if (num == 100 && t == 0) {
1257 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1258 num = 0;
1259 } else if (num < 1000) {
1260 int hundreds = num / 100;
1261 num = num % 100;
1262 if (hundreds == 1) {
1263 ast_copy_string(fn, "digits/1N", sizeof(fn));
1264 } else {
1265 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1266 }
1267 ast_copy_string(fna, "digits/hundred", sizeof(fna));
1268 } else if (num == 1000 && t == 0) {
1269 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1270 num = 0;
1271 } else if (num < 1000000) {
1272 int thousands = num / 1000;
1273 num = num % 1000;
1274 t = 1;
1275 if (thousands == 1) {
1276 ast_copy_string(fn, "digits/1N", sizeof(fn));
1277 ast_copy_string(fna, "digits/thousand", sizeof(fna));
1278 } else {
1279 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
1280 if (res)
1281 return res;
1282 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1283 }
1284 } else if (num < 1000000000) {
1285 int millions = num / 1000000;
1286 num = num % 1000000;
1287 t = 1;
1288 if (millions == 1) {
1289 ast_copy_string(fn, "digits/1F", sizeof(fn));
1290 ast_copy_string(fna, "digits/million", sizeof(fna));
1291 } else {
1292 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
1293 if (res)
1294 return res;
1295 ast_copy_string(fn, "digits/millions", sizeof(fn));
1296 }
1297 } else if (num <= INT_MAX) {
1298 int billions = num / 1000000000;
1299 num = num % 1000000000;
1300 t = 1;
1301 if (billions == 1) {
1302 ast_copy_string(fn, "digits/1F", sizeof(fn));
1303 ast_copy_string(fna, "digits/milliard", sizeof(fna));
1304 } else {
1305 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
1306 if (res) {
1307 return res;
1308 }
1309 ast_copy_string(fn, "digits/milliards", sizeof(fn));
1310 }
1311 } else {
1312 ast_debug(1, "Number '%d' is too big for me\n", num);
1313 res = -1;
1314 }
1315 if (!res) {
1316 if (!ast_streamfile(chan, fn, language)) {
1317 if ((audiofd > -1) && (ctrlfd > -1))
1318 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1319 else
1320 res = ast_waitstream(chan, ints);
1321 }
1322 ast_stopstream(chan);
1323 if (!res) {
1324 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1325 if ((audiofd > -1) && (ctrlfd > -1))
1326 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1327 else
1328 res = ast_waitstream(chan, ints);
1329 }
1330 ast_stopstream(chan);
1331 strcpy(fna, "");
1332 }
1333 }
1334 }
1335 return res;
1336}
1337
1338/*! \brief ast_say_number_full_ps: Pashto syntax
1339
1340 New files:
1341 In addition to English, the following sounds are required:
1342 - wesht
1343 - sawa
1344 - zara
1345 */
1346static int ast_say_number_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1347{
1348 int res = 0, t = 0;
1349 char fn[256] = "";
1350 char fna[256] = "";
1351 int playWesht = 0;
1352 if (!num){
1353 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1354 }
1355
1356 while (!res && num) {
1357 /* The grammar for Pashto numbers is the same as for English except
1358 * for the following:
1359 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1360 * "one-and twenty" and 68 is "eight-and sixty".
1361 * - 20 has unique pronounciation if there is a unit number after it
1362 * i.e. 21 is one wesht while 20 is just "twenty"
1363 * - 200 is called sawa
1364 * - 2000 is called zara
1365 */
1366 if (num < 0) {
1367 ast_copy_string(fn, "digits/minus", sizeof(fn));
1368 if ( num > INT_MIN ) {
1369 num = -num;
1370 } else {
1371 num = 0;
1372 }
1373 } else if (num < 20) {
1374 snprintf(fn, sizeof(fn), "digits/%d", num);
1375 num = 0;
1376 } else if (num < 100) {
1377 int ones = num % 10;
1378 if (ones) {
1379 snprintf(fn, sizeof(fn), "digits/%d", ones);
1380 num -= ones;
1381 if (num == 20){
1382 playWesht = 1;
1383 }
1384 } else {
1385 if (num == 20 && playWesht == 1){
1386 snprintf(fn, sizeof(fn), "digits/wesht");
1387 playWesht = 0;
1388 } else{
1389 snprintf(fn, sizeof(fn), "digits/%d", num);
1390 }
1391 num = 0;
1392 }
1393 } else if (num == 100 && t == 0) {
1394 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1395 num = 0;
1396 } else if (num < 1000) {
1397 int hundreds = num / 100;
1398 num = num % 100;
1399 /* 100: hundreds = 1, num = 0
1400 * 101: hundreds = 1, num = 1
1401 * 200: hundreds = 2, num = 0
1402 * 202: hundreds = 2, num = 2
1403 */
1404 if (hundreds == 1){
1405 if (num){
1406 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1407 }
1408 ast_copy_string(fna, "digits/hundred", sizeof(fna));
1409 } else{
1410 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1411 ast_copy_string(fna, "digits/sawa", sizeof(fna));
1412 }
1413 } else if (num == 1000 && t == 0) {
1414 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1415 num = 0;
1416 } else if (num < 1000000) {
1417 int thousands = num / 1000;
1418 num = num % 1000;
1419 t = 1;
1420 if (thousands == 1) {
1421 if (num){
1422 snprintf(fn, sizeof(fn), "digits/%d", thousands);
1423 }
1424 ast_copy_string(fna, "digits/thousand", sizeof(fna));
1425 } else {
1426 res = ast_say_number_full_ps(chan, thousands, ints, language, options, audiofd, ctrlfd);
1427 if (res)
1428 return res;
1429 ast_copy_string(fn, "digits/zara", sizeof(fn));
1430 }
1431 } else if (num < 1000000000) {
1432 int millions = num / 1000000;
1433 num = num % 1000000;
1434 t = 1;
1435 if (millions == 1) {
1436 ast_copy_string(fna, "digits/million", sizeof(fna));
1437 } else {
1438 res = ast_say_number_full_ps(chan, millions, ints, language, options, audiofd, ctrlfd);
1439 if (res)
1440 return res;
1441 ast_copy_string(fn, "digits/millions", sizeof(fn));
1442 }
1443 } else if (num <= INT_MAX) {
1444 int billions = num / 1000000000;
1445 num = num % 1000000000;
1446 t = 1;
1447 if (billions == 1) {
1448 ast_copy_string(fna, "digits/milliard", sizeof(fna));
1449 } else {
1450 res = ast_say_number_full_ps(chan, billions, ints, language, options, audiofd, ctrlfd);
1451 if (res) {
1452 return res;
1453 }
1454 ast_copy_string(fn, "digits/milliards", sizeof(fn));
1455 }
1456 } else {
1457 ast_debug(1, "Number '%d' is too big for me\n", num);
1458 res = -1;
1459 }
1460 if (!res) {
1461 if (!ast_streamfile(chan, fn, language)) {
1462 if ((audiofd > -1) && (ctrlfd > -1))
1463 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1464 else
1465 res = ast_waitstream(chan, ints);
1466 }
1467 ast_stopstream(chan);
1468 if (!res) {
1469 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1470 if ((audiofd > -1) && (ctrlfd > -1))
1471 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1472 else
1473 res = ast_waitstream(chan, ints);
1474 }
1475 ast_stopstream(chan);
1476 strcpy(fna, "");
1477 }
1478 }
1479 }
1480 return res;
1481}
1482
1483/*! \brief ast_say_number_full_en_GB: British syntax
1484 New files:
1485 - In addition to American English, the following sounds are required: "vm-and"
1486 */
1487static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1488{
1489 int res = 0;
1490 int playh = 0;
1491 int playa = 0;
1492 char fn[256] = "";
1493 if (!num)
1494 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1495
1496 while (!res && (num || playh || playa )) {
1497 if (num < 0) {
1498 ast_copy_string(fn, "digits/minus", sizeof(fn));
1499 if ( num > INT_MIN ) {
1500 num = -num;
1501 } else {
1502 num = 0;
1503 }
1504 } else if (playh) {
1505 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1506 playh = 0;
1507 } else if (playa) {
1508 ast_copy_string(fn, find_first_and(language), sizeof(fn));
1509 playa = 0;
1510 } else if (num < 20) {
1511 snprintf(fn, sizeof(fn), "digits/%d", num);
1512 num = 0;
1513 } else if (num < 100) {
1514 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1515 num %= 10;
1516 } else if (num < 1000) {
1517 int hundreds = num / 100;
1518 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1519
1520 playh++;
1521 num -= 100 * hundreds;
1522 if (num)
1523 playa++;
1524 } else if (num < 1000000) {
1525 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
1526 if (res)
1527 return res;
1528 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1529 num %= 1000;
1530 if (num && num < 100)
1531 playa++;
1532 } else if (num < 1000000000) {
1533 int millions = num / 1000000;
1534 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1535 if (res)
1536 return res;
1537 ast_copy_string(fn, "digits/million", sizeof(fn));
1538 num %= 1000000;
1539 if (num && num < 100)
1540 playa++;
1541 } else {
1542 ast_debug(1, "Number '%d' is too big for me\n", num);
1543 res = -1;
1544 }
1545
1546 if (!res) {
1547 if (!ast_streamfile(chan, fn, language)) {
1548 if ((audiofd > -1) && (ctrlfd > -1))
1549 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1550 else
1551 res = ast_waitstream(chan, ints);
1552 }
1553 ast_stopstream(chan);
1554 }
1555 }
1556 return res;
1557}
1558
1559/*! \brief ast_say_number_full_en_dr: Dari syntax
1560 New files:
1561 - In addition to American English, the following sounds are required: "o"
1562 */
1563static int ast_say_number_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1564{
1565 int res = 0;
1566 int playh = 0;
1567 int playa = 0;
1568 int say_o = 0;
1569 char fn[256] = "";
1570 if (!num)
1571 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1572
1573 while (!res && (num || playh || playa )) {
1574 if (num < 0) {
1575 ast_copy_string(fn, "digits/minus", sizeof(fn));
1576 if ( num > INT_MIN ) {
1577 num = -num;
1578 } else {
1579 num = 0;
1580 }
1581 } else if (playh) {
1582 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1583 playh = 0;
1584 } else if (playa) {
1585 ast_copy_string(fn, "digits/o", sizeof(fn));
1586 playa = 0;
1587 } else if (num < 20) {
1588 snprintf(fn, sizeof(fn), "digits/%d", num);
1589 num = 0;
1590 } else if (num < 100) {
1591 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1592 num %= 10;
1593 if (num && say_o)
1594 playa++;
1595 } else if (num < 1000) {
1596 int hundreds = num / 100;
1597 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1598
1599 playh++;
1600 num -= 100 * hundreds;
1601 if (num && say_o)
1602 playa++;
1603 } else if (num < 1000000) {
1604 res = ast_say_number_full_dr(chan, num / 1000, ints, language, audiofd, ctrlfd);
1605 if (res)
1606 return res;
1607 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1608 num %= 1000;
1609 if (num && say_o)
1610 playa++;
1611 } else if (num < 1000000000) {
1612 int millions = num / 1000000;
1613 res = ast_say_number_full_dr(chan, millions, ints, language, audiofd, ctrlfd);
1614 if (res)
1615 return res;
1616 ast_copy_string(fn, "digits/million", sizeof(fn));
1617 num %= 1000000;
1618 if (num && num < 100 && say_o)
1619 playa++;
1620 } else {
1621 ast_debug(1, "Number '%d' is too big for me\n", num);
1622 res = -1;
1623 }
1624
1625 if (!res) {
1626 if (!ast_streamfile(chan, fn, language)) {
1627 if ((audiofd > -1) && (ctrlfd > -1))
1628 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1629 else
1630 res = ast_waitstream(chan, ints);
1631 }
1632 ast_stopstream(chan);
1633 }
1634 }
1635 return res;
1636}
1637
1638/*! \brief ast_say_number_full_es: Spanish syntax
1639
1640 New files:
1641 Requires a few new audios:
1642 1F.gsm: feminine 'una'
1643 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
1644 */
1645static 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)
1646{
1647 int res = 0;
1648 int playa = 0;
1649 int mf = 0; /* +1 = male; -1 = female */
1650 char fn[256] = "";
1651 if (!num)
1652 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1653
1654 if (options) {
1655 if (!strncasecmp(options, "f", 1))
1656 mf = -1;
1657 else if (!strncasecmp(options, "m", 1))
1658 mf = 1;
1659 }
1660
1661 while (!res && num) {
1662 if (num < 0) {
1663 ast_copy_string(fn, "digits/minus", sizeof(fn));
1664 if ( num > INT_MIN ) {
1665 num = -num;
1666 } else {
1667 num = 0;
1668 }
1669 } else if (playa) {
1670 ast_copy_string(fn, find_first_and(language), sizeof(fn));
1671 playa = 0;
1672 } else if (num == 1) {
1673 if (mf < 0)
1674 snprintf(fn, sizeof(fn), "digits/%dF", num);
1675 else if (mf > 0)
1676 snprintf(fn, sizeof(fn), "digits/%dM", num);
1677 else
1678 snprintf(fn, sizeof(fn), "digits/%d", num);
1679 num = 0;
1680 } else if (num < 31) {
1681 snprintf(fn, sizeof(fn), "digits/%d", num);
1682 num = 0;
1683 } else if (num < 100) {
1684 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1685 num %= 10;
1686 if (num)
1687 playa++;
1688 } else if (num == 100) {
1689 ast_copy_string(fn, "digits/100", sizeof(fn));
1690 num = 0;
1691 } else if (num < 200) {
1692 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1693 num -= 100;
1694 } else {
1695 if (num < 1000) {
1696 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1697 num %= 100;
1698 } else if (num < 2000) {
1699 num %= 1000;
1700 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1701 } else {
1702 if (num < 1000000) {
1703 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1704 if (res)
1705 return res;
1706 num %= 1000;
1707 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1708 } else {
1709 if (num < 2147483640) {
1710 if ((num/1000000) == 1) {
1711 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1712 if (res)
1713 return res;
1714 ast_copy_string(fn, "digits/million", sizeof(fn));
1715 } else {
1716 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1717 if (res)
1718 return res;
1719 ast_copy_string(fn, "digits/millions", sizeof(fn));
1720 }
1721 num %= 1000000;
1722 } else {
1723 ast_debug(1, "Number '%d' is too big for me\n", num);
1724 res = -1;
1725 }
1726 }
1727 }
1728 }
1729
1730 if (!res) {
1731 if (!ast_streamfile(chan, fn, language)) {
1732 if ((audiofd > -1) && (ctrlfd > -1))
1733 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1734 else
1735 res = ast_waitstream(chan, ints);
1736 }
1737 ast_stopstream(chan);
1738
1739 }
1740
1741 }
1742 return res;
1743}
1744
1745/*! \brief ast_say_number_full_fr: French syntax
1746 Extra sounds needed:
1747 1F: feminin 'une'
1748 et: 'and' */
1749static 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)
1750{
1751 int res = 0;
1752 int playh = 0;
1753 int playa = 0;
1754 int mf = 1; /* +1 = male; -1 = female */
1755 char fn[256] = "";
1756 if (!num)
1757 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1758
1759 if (options && !strncasecmp(options, "f", 1))
1760 mf = -1;
1761
1762 while (!res && (num || playh || playa)) {
1763 if (num < 0) {
1764 ast_copy_string(fn, "digits/minus", sizeof(fn));
1765 if ( num > INT_MIN ) {
1766 num = -num;
1767 } else {
1768 num = 0;
1769 }
1770 } else if (playh) {
1771 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1772 playh = 0;
1773 } else if (playa) {
1774 ast_copy_string(fn, "digits/et", sizeof(fn));
1775 playa = 0;
1776 } else if (num == 1) {
1777 if (mf < 0)
1778 snprintf(fn, sizeof(fn), "digits/%dF", num);
1779 else
1780 snprintf(fn, sizeof(fn), "digits/%d", num);
1781 num = 0;
1782 } else if (num < 21) {
1783 snprintf(fn, sizeof(fn), "digits/%d", num);
1784 num = 0;
1785 } else if (num < 70) {
1786 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1787 if ((num % 10) == 1) playa++;
1788 num = num % 10;
1789 } else if (num < 80) {
1790 ast_copy_string(fn, "digits/60", sizeof(fn));
1791 if ((num % 10) == 1) playa++;
1792 num -= 60;
1793 } else if (num < 100) {
1794 ast_copy_string(fn, "digits/80", sizeof(fn));
1795 num = num - 80;
1796 } else if (num < 200) {
1797 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1798 num = num - 100;
1799 } else if (num < 1000) {
1800 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1801 playh++;
1802 num = num % 100;
1803 } else if (num < 2000) {
1804 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1805 num = num - 1000;
1806 } else if (num < 1000000) {
1807 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1808 if (res)
1809 return res;
1810 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1811 num = num % 1000;
1812 } else if (num < 1000000000) {
1813 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1814 if (res)
1815 return res;
1816 ast_copy_string(fn, "digits/million", sizeof(fn));
1817 num = num % 1000000;
1818 } else {
1819 ast_debug(1, "Number '%d' is too big for me\n", num);
1820 res = -1;
1821 }
1822 if (!res) {
1823 if (!ast_streamfile(chan, fn, language)) {
1824 if ((audiofd > -1) && (ctrlfd > -1))
1825 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1826 else
1827 res = ast_waitstream(chan, ints);
1828 }
1829 ast_stopstream(chan);
1830 }
1831 }
1832 return res;
1833}
1834
1835
1836
1837/* Hebrew syntax
1838 * Check doc/lang/hebrew-digits.txt for information about the various
1839 * recordings required to make this translation work properly */
1840#define SAY_NUM_BUF_SIZE 256
1841static 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)
1842{
1843 int res = 0;
1844 int state = 0; /* no need to save anything */
1845 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1846 int tmpnum = 0;
1847
1848 char fn[SAY_NUM_BUF_SIZE] = "";
1849
1850 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1851
1852 if (!num) {
1853 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1854 }
1855 if (options && !strncasecmp(options, "m", 1)) {
1856 mf = 1;
1857 }
1858 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1859
1860 /* Do we have work to do? */
1861 while (!res && (num || (state > 0))) {
1862 /* first type of work: play a second sound. In this loop
1863 * we can only play one sound file at a time. Thus playing
1864 * a second one requires repeating the loop just for the
1865 * second file. The variable 'state' remembers where we were.
1866 * state==0 is the normal mode and it means that we continue
1867 * to check if the number num has yet anything left.
1868 */
1869 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1870
1871 if (state == 1) {
1872 state = 0;
1873 } else if (state == 2) {
1874 if ((num >= 11) && (num < 21)) {
1875 if (mf < 0) {
1876 snprintf(fn, sizeof(fn), "digits/ve");
1877 } else {
1878 snprintf(fn, sizeof(fn), "digits/uu");
1879 }
1880 } else {
1881 switch (num) {
1882 case 1:
1883 snprintf(fn, sizeof(fn), "digits/ve");
1884 break;
1885 case 2:
1886 snprintf(fn, sizeof(fn), "digits/uu");
1887 break;
1888 case 3:
1889 if (mf < 0) {
1890 snprintf(fn, sizeof(fn), "digits/ve");
1891 } else {
1892 snprintf(fn, sizeof(fn), "digits/uu");
1893 }
1894 break;
1895 case 4:
1896 snprintf(fn, sizeof(fn), "digits/ve");
1897 break;
1898 case 5:
1899 snprintf(fn, sizeof(fn), "digits/ve");
1900 break;
1901 case 6:
1902 snprintf(fn, sizeof(fn), "digits/ve");
1903 break;
1904 case 7:
1905 snprintf(fn, sizeof(fn), "digits/ve");
1906 break;
1907 case 8:
1908 snprintf(fn, sizeof(fn), "digits/uu");
1909 break;
1910 case 9:
1911 snprintf(fn, sizeof(fn), "digits/ve");
1912 break;
1913 case 10:
1914 snprintf(fn, sizeof(fn), "digits/ve");
1915 break;
1916 }
1917 }
1918 state = 0;
1919 } else if (state == 3) {
1920 snprintf(fn, sizeof(fn), "digits/1k");
1921 state = 0;
1922 } else if (num < 0) {
1923 snprintf(fn, sizeof(fn), "digits/minus");
1924 num = (-1) * num;
1925 } else if (num < 20) {
1926 if (mf < 0) {
1927 snprintf(fn, sizeof(fn), "digits/%d", num);
1928 } else {
1929 snprintf(fn, sizeof(fn), "digits/%dm", num);
1930 }
1931 num = 0;
1932 } else if ((num < 100) && (num >= 20)) {
1933 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1934 num = num % 10;
1935 if (num > 0) {
1936 state = 2;
1937 }
1938 } else if ((num >= 100) && (num < 1000)) {
1939 tmpnum = num / 100;
1940 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1941 num = num - (tmpnum * 100);
1942 if ((num > 0) && (num < 11)) {
1943 state = 2;
1944 }
1945 } else if ((num >= 1000) && (num < 10000)) {
1946 tmpnum = num / 1000;
1947 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1948 num = num - (tmpnum * 1000);
1949 if ((num > 0) && (num < 11)) {
1950 state = 2;
1951 }
1952 } else if (num < 20000) {
1953 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1954 num = num % 1000;
1955 state = 3;
1956 } else if (num < 1000000) {
1957 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1958 if (res) {
1959 return res;
1960 }
1961 snprintf(fn, sizeof(fn), "digits/1k");
1962 num = num % 1000;
1963 if ((num > 0) && (num < 11)) {
1964 state = 2;
1965 }
1966 } else if (num < 2000000) {
1967 snprintf(fn, sizeof(fn), "digits/million");
1968 num = num % 1000000;
1969 if ((num > 0) && (num < 11)) {
1970 state = 2;
1971 }
1972 } else if (num < 3000000) {
1973 snprintf(fn, sizeof(fn), "digits/twomillion");
1974 num = num - 2000000;
1975 if ((num > 0) && (num < 11)) {
1976 state = 2;
1977 }
1978 } else if (num < 1000000000) {
1979 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1980 if (res) {
1981 return res;
1982 }
1983 snprintf(fn, sizeof(fn), "digits/million");
1984 num = num % 1000000;
1985 if ((num > 0) && (num < 11)) {
1986 state = 2;
1987 }
1988 } else {
1989 ast_debug(1, "Number '%d' is too big for me\n", num);
1990 res = -1;
1991 }
1992 tmpnum = 0;
1993 if (!res) {
1994 if (!ast_streamfile(chan, fn, language)) {
1995 if ((audiofd > -1) && (ctrlfd > -1)) {
1996 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1997 } else {
1998 res = ast_waitstream(chan, ints);
1999 }
2000 }
2001 ast_stopstream(chan);
2002 }
2003 }
2004 return res;
2005}
2006
2007/*! \brief ast_say_number_full_hu: Hungarian syntax
2008
2009 Extra sounds needed:
2010 10en: "tizen"
2011 20on: "huszon"
2012*/
2013static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2014{
2015 int res = 0;
2016 int playh = 0;
2017 char fn[256] = "";
2018 if (!num)
2019 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2020
2021 /*
2022 Hungarian support
2023 like english, except numbers up to 29 are from 2 words.
2024 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
2025 */
2026
2027 while(!res && (num || playh)) {
2028 if (num < 0) {
2029 ast_copy_string(fn, "digits/minus", sizeof(fn));
2030 if ( num > INT_MIN ) {
2031 num = -num;
2032 } else {
2033 num = 0;
2034 }
2035 } else if (playh) {
2036 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2037 playh = 0;
2038 } else if (num < 11 || num == 20) {
2039 snprintf(fn, sizeof(fn), "digits/%d", num);
2040 num = 0;
2041 } else if (num < 20) {
2042 ast_copy_string(fn, "digits/10en", sizeof(fn));
2043 num -= 10;
2044 } else if (num < 30) {
2045 ast_copy_string(fn, "digits/20on", sizeof(fn));
2046 num -= 20;
2047 } else if (num < 100) {
2048 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2049 num %= 10;
2050 } else {
2051 if (num < 1000){
2052 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2053 playh++;
2054 num %= 100;
2055 } else {
2056 if (num < 1000000) { /* 1,000,000 */
2057 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
2058 if (res)
2059 return res;
2060 num %= 1000;
2061 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2062 } else {
2063 if (num < 1000000000) { /* 1,000,000,000 */
2064 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2065 if (res)
2066 return res;
2067 num %= 1000000;
2068 ast_copy_string(fn, "digits/million", sizeof(fn));
2069 } else {
2070 ast_debug(1, "Number '%d' is too big for me\n", num);
2071 res = -1;
2072 }
2073 }
2074 }
2075 }
2076 if (!res) {
2077 if(!ast_streamfile(chan, fn, language)) {
2078 if ((audiofd > -1) && (ctrlfd > -1))
2079 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2080 else
2081 res = ast_waitstream(chan, ints);
2082 }
2083 ast_stopstream(chan);
2084 }
2085 }
2086 return res;
2087}
2088
2089/*! \brief ast_say_number_full_is: Icelandic syntax */
2090/* New files:
2091 In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk"
2092 */
2093static 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)
2094{
2095 int res = 0;
2096 int playh = 0;
2097 int playa = 0;
2098 int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */
2099 char fn[256] = "";
2100
2101 if (!num)
2102 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2103
2104 if (options && !strncasecmp(options, "f", 1)) cn = 2;
2105 if (options && !strncasecmp(options, "c", 1)) cn = 3;
2106 /* It seems that sometimes people are using c and sometimes n. */
2107 if (options && !strncasecmp(options, "n", 1)) cn = 3;
2108
2109 while (!res && (num || playh || playa )) {
2110 if (num < 0) {
2111 ast_copy_string(fn, "digits/minus", sizeof(fn));
2112 if ( num > INT_MIN ) {
2113 num = -num;
2114 } else {
2115 num = 0;
2116 }
2117 } else if (playh) {
2118 if (playh > 1)
2119 ast_copy_string(fn, "digits/hundreds", sizeof(fn));
2120 else
2121 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2122 playh = 0;
2123 } else if (playa) {
2124 ast_copy_string(fn, find_first_and(language), sizeof(fn));
2125 playa = 0;
2126 } else if (num < 5 && cn == 2) {
2127 snprintf(fn, sizeof(fn), "digits/%dkvk", num);
2128 num = 0;
2129 } else if (num < 5 && cn == 3) {
2130 snprintf(fn, sizeof(fn), "digits/%dhk", num);
2131 num = 0;
2132 } else if (num < 20) {
2133 snprintf(fn, sizeof(fn), "digits/%d", num);
2134 num = 0;
2135 } else if (num < 100) {
2136 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2137 num %= 10;
2138 if (num)
2139 playa++;
2140 } else if (num < 1000) {
2141 int hundreds = num / 100;
2142 /* The number prepending hundreds are in neutral */
2143 if (hundreds < 5)
2144 snprintf(fn, sizeof(fn), "digits/%dhk", hundreds);
2145 else
2146 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2147
2148 playh = hundreds;
2149 num -= 100 * hundreds;
2150 if (num && num < 20)
2151 playa++;
2152 /* The 'and' moves forward on even tens. */
2153 if (num && (num % 10) == 0)
2154 playa++;
2155 } else if (num < 1000000) {
2156 res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2157 /* Play 'and' if it's an even hundred. */
2158 if ((num % 100) == 0 && (num % 1000 != 0)) {
2159 playa++;
2160 }
2161 if (res)
2162 return res;
2163 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2164 num %= 1000;
2165 if (num && (num < 20 || (num % 10 == 0)))
2166 playa++;
2167 } else if (num < 1000000000) {
2168 int millions = num / 1000000;
2169 /* The number of millions is feminine */
2170 res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd);
2171 if (res)
2172 return res;
2173 if (millions > 1)
2174 ast_copy_string(fn, "digits/millions", sizeof(fn));
2175 else
2176 ast_copy_string(fn, "digits/million", sizeof(fn));
2177 num %= 1000000;
2178 if (num && num < 100)
2179 playa++;
2180 } else if (num < INT_MAX) {
2181 int milliards = num / 1000000000;
2182 /* The number of milliards is masculine */
2183 res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd);
2184 if (res)
2185 return res;
2186 if (milliards > 1)
2187 ast_copy_string(fn, "digits/milliards", sizeof(fn));
2188 else
2189 ast_copy_string(fn, "digits/milliard", sizeof(fn));
2190 num %= 1000000000;
2191 if (num && num < 100)
2192 playa++;
2193 } else {
2194 ast_debug(1, "Number '%d' is too big for me\n", num);
2195 res = -1;
2196 }
2197
2198 if (!res) {
2199 if (!ast_streamfile(chan, fn, language)) {
2200 if ((audiofd > -1) && (ctrlfd > -1))
2201 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2202 else
2203 res = ast_waitstream(chan, ints);
2204 }
2205 ast_stopstream(chan);
2206 }
2207 }
2208 return res;
2209}
2210
2211
2212/*! \brief ast_say_number_full_it: Italian */
2213static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2214{
2215 int res = 0;
2216 int playh = 0;
2217 int tempnum = 0;
2218 char fn[256] = "";
2219
2220 if (!num)
2221 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2222
2223 /*
2224 Italian support
2225
2226 Like english, numbers up to 20 are a single 'word', and others
2227 compound, but with exceptions.
2228 For example 21 is not twenty-one, but there is a single word in 'it'.
2229 Idem for 28 (ie when a the 2nd part of a compound number
2230 starts with a vowel)
2231
2232 There are exceptions also for hundred, thousand and million.
2233 In english 100 = one hundred, 200 is two hundred.
2234 In italian 100 = cento , like to say hundred (without one),
2235 200 and more are like english.
2236
2237 Same applies for thousand:
2238 1000 is one thousand in en, 2000 is two thousand.
2239 In it we have 1000 = mille , 2000 = 2 mila
2240
2241 For million(s) we use the plural, if more than one
2242 Also, one million is abbreviated in it, like on-million,
2243 or 'un milione', not 'uno milione'.
2244 So the right file is provided.
2245 */
2246
2247 while (!res && (num || playh)) {
2248 if (num < 0) {
2249 ast_copy_string(fn, "digits/minus", sizeof(fn));
2250 if ( num > INT_MIN ) {
2251 num = -num;
2252 } else {
2253 num = 0;
2254 }
2255 } else if (playh) {
2256 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2257 playh = 0;
2258 } else if (num < 20) {
2259 snprintf(fn, sizeof(fn), "digits/%d", num);
2260 num = 0;
2261 } else if (num == 21) {
2262 snprintf(fn, sizeof(fn), "digits/%d", num);
2263 num = 0;
2264 } else if (num == 28) {
2265 snprintf(fn, sizeof(fn), "digits/%d", num);
2266 num = 0;
2267 } else if (num == 31) {
2268 snprintf(fn, sizeof(fn), "digits/%d", num);
2269 num = 0;
2270 } else if (num == 38) {
2271 snprintf(fn, sizeof(fn), "digits/%d", num);
2272 num = 0;
2273 } else if (num == 41) {
2274 snprintf(fn, sizeof(fn), "digits/%d", num);
2275 num = 0;
2276 } else if (num == 48) {
2277 snprintf(fn, sizeof(fn), "digits/%d", num);
2278 num = 0;
2279 } else if (num == 51) {
2280 snprintf(fn, sizeof(fn), "digits/%d", num);
2281 num = 0;
2282 } else if (num == 58) {
2283 snprintf(fn, sizeof(fn), "digits/%d", num);
2284 num = 0;
2285 } else if (num == 61) {
2286 snprintf(fn, sizeof(fn), "digits/%d", num);
2287 num = 0;
2288 } else if (num == 68) {
2289 snprintf(fn, sizeof(fn), "digits/%d", num);
2290 num = 0;
2291 } else if (num == 71) {
2292 snprintf(fn, sizeof(fn), "digits/%d", num);
2293 num = 0;
2294 } else if (num == 78) {
2295 snprintf(fn, sizeof(fn), "digits/%d", num);
2296 num = 0;
2297 } else if (num == 81) {
2298 snprintf(fn, sizeof(fn), "digits/%d", num);
2299 num = 0;
2300 } else if (num == 88) {
2301 snprintf(fn, sizeof(fn), "digits/%d", num);
2302 num = 0;
2303 } else if (num == 91) {
2304 snprintf(fn, sizeof(fn), "digits/%d", num);
2305 num = 0;
2306 } else if (num == 98) {
2307 snprintf(fn, sizeof(fn), "digits/%d", num);
2308 num = 0;
2309 } else if (num < 100) {
2310 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2311 num %= 10;
2312 } else {
2313 if (num < 1000) {
2314 if ((num / 100) > 1) {
2315 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2316 playh++;
2317 } else {
2318 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2319 }
2320 num %= 100;
2321 } else {
2322 if (num < 1000000) { /* 1,000,000 */
2323 if ((num/1000) > 1)
2324 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
2325 if (res)
2326 return res;
2327 tempnum = num;
2328 num %= 1000;
2329 if ((tempnum / 1000) < 2)
2330 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2331 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
2332 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2333 } else {
2334 if (num < 1000000000) { /* 1,000,000,000 */
2335 if ((num / 1000000) > 1)
2336 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2337 if (res)
2338 return res;
2339 tempnum = num;
2340 num %= 1000000;
2341 if ((tempnum / 1000000) < 2)
2342 ast_copy_string(fn, "digits/million", sizeof(fn));
2343 else
2344 ast_copy_string(fn, "digits/millions", sizeof(fn));
2345 } else {
2346 ast_debug(1, "Number '%d' is too big for me\n", num);
2347 res = -1;
2348 }
2349 }
2350 }
2351 }
2352 if (!res) {
2353 if (!ast_streamfile(chan, fn, language)) {
2354 if ((audiofd > -1) && (ctrlfd > -1))
2355 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2356 else
2357 res = ast_waitstream(chan, ints);
2358 }
2359 ast_stopstream(chan);
2360 }
2361 }
2362 return res;
2363}
2364
2365/*! \brief ast_say_number_full_nl: dutch syntax
2366 * New files: digits/nl-en
2367 */
2368static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2369{
2370 int res = 0;
2371 int playh = 0;
2372 int units = 0;
2373 char fn[256] = "";
2374 if (!num)
2375 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2376 while (!res && (num || playh )) {
2377 if (num < 0) {
2378 ast_copy_string(fn, "digits/minus", sizeof(fn));
2379 if ( num > INT_MIN ) {
2380 num = -num;
2381 } else {
2382 num = 0;
2383 }
2384 } else if (playh) {
2385 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2386 playh = 0;
2387 } else if (num < 20) {
2388 snprintf(fn, sizeof(fn), "digits/%d", num);
2389 num = 0;
2390 } else if (num < 100) {
2391 units = num % 10;
2392 if (units > 0) {
2393 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
2394 if (res)
2395 return res;
2396 num = num - units;
2397 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
2398 } else {
2399 snprintf(fn, sizeof(fn), "digits/%d", num - units);
2400 num = 0;
2401 }
2402 } else if (num < 200) {
2403 /* hundred, not one-hundred */
2404 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2405 num %= 100;
2406 } else if (num < 1000) {
2407 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
2408 playh++;
2409 num %= 100;
2410 } else {
2411 if (num < 1100) {
2412 /* thousand, not one-thousand */
2413 num %= 1000;
2414 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2415 } else if (num < 10000) { /* 1,100 to 9,9999 */
2416 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
2417 if (res)
2418 return res;
2419 num %= 100;
2420 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2421 } else {
2422 if (num < 1000000) { /* 1,000,000 */
2423 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
2424 if (res)
2425 return res;
2426 num %= 1000;
2427 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2428 } else {
2429 if (num < 1000000000) { /* 1,000,000,000 */
2430 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2431 if (res)
2432 return res;
2433 num %= 1000000;
2434 ast_copy_string(fn, "digits/million", sizeof(fn));
2435 } else {
2436 ast_debug(1, "Number '%d' is too big for me\n", num);
2437 res = -1;
2438 }
2439 }
2440 }
2441 }
2442
2443 if (!res) {
2444 if (!ast_streamfile(chan, fn, language)) {
2445 if ((audiofd > -1) && (ctrlfd > -1))
2446 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2447 else
2448 res = ast_waitstream(chan, ints);
2449 }
2450 ast_stopstream(chan);
2451 }
2452 }
2453 return res;
2454}
2455
2456/*! \brief ast_say_number_full_no: Norwegian syntax
2457 * New files:
2458 * In addition to American English, the following sounds are required: "and", "1N"
2459 *
2460 * The grammar for Norwegian numbers is the same as for English except
2461 * for the following:
2462 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
2463 * "and" before the last two digits, i.e. 2034 is "two thousand and
2464 * thirty-four" and 1000012 is "one million and twelve".
2465 */
2466static 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)
2467{
2468 int res = 0;
2469 int playh = 0;
2470 int playa = 0;
2471 int cn = 1; /* +1 = commune; -1 = neuter */
2472 char fn[256] = "";
2473
2474 if (!num)
2475 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2476
2477 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2478
2479 while (!res && (num || playh || playa )) {
2480 if (num < 0) {
2481 ast_copy_string(fn, "digits/minus", sizeof(fn));
2482 if ( num > INT_MIN ) {
2483 num = -num;
2484 } else {
2485 num = 0;
2486 }
2487 } else if (playh) {
2488 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2489 playh = 0;
2490 } else if (playa) {
2491 ast_copy_string(fn, find_first_and(language), sizeof(fn));
2492 playa = 0;
2493 } else if (num == 1 && cn == -1) {
2494 ast_copy_string(fn, "digits/1N", sizeof(fn));
2495 num = 0;
2496 } else if (num < 20) {
2497 snprintf(fn, sizeof(fn), "digits/%d", num);
2498 num = 0;
2499 } else if (num < 100) {
2500 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2501 num %= 10;
2502 } else if (num < 1000) {
2503 int hundreds = num / 100;
2504 if (hundreds == 1)
2505 ast_copy_string(fn, "digits/1N", sizeof(fn));
2506 else
2507 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2508
2509 playh++;
2510 num -= 100 * hundreds;
2511 if (num)
2512 playa++;
2513 } else if (num < 1000000) {
2514 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2515 if (res)
2516 return res;
2517 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2518 num %= 1000;
2519 if (num && num < 100)
2520 playa++;
2521 } else if (num < 1000000000) {
2522 int millions = num / 1000000;
2523 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
2524 if (res)
2525 return res;
2526 ast_copy_string(fn, "digits/million", sizeof(fn));
2527 num %= 1000000;
2528 if (num && num < 100)
2529 playa++;
2530 } else {
2531 ast_debug(1, "Number '%d' is too big for me\n", num);
2532 res = -1;
2533 }
2534
2535 if (!res) {
2536 if (!ast_streamfile(chan, fn, language)) {
2537 if ((audiofd > -1) && (ctrlfd > -1))
2538 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2539 else
2540 res = ast_waitstream(chan, ints);
2541 }
2542 ast_stopstream(chan);
2543 }
2544 }
2545 return res;
2546}
2547
2548typedef struct {
2550 char *cyfry[10];
2551 char *cyfry2[10];
2552 char *setki[10];
2553 char *dziesiatki[10];
2554 char *nastki[10];
2555 char *rzedy[3][3];
2556} odmiana;
2557
2558static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
2559{
2560 if (rzad==0)
2561 return "";
2562
2563 if (i==1)
2564 return odm->rzedy[rzad - 1][0];
2565 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
2566 return odm->rzedy[rzad - 1][1];
2567 else
2568 return odm->rzedy[rzad - 1][2];
2569}
2570
2571static char* pl_append(char* buffer, char* str)
2572{
2573 strcpy(buffer, str);
2574 buffer += strlen(str);
2575 return buffer;
2576}
2577
2578static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
2579{
2580 char file_name[255] = "digits/";
2581 strcat(file_name, fn);
2582 ast_debug(1, "Trying to play: %s\n", file_name);
2583 if (!ast_streamfile(chan, file_name, language)) {
2584 if ((audiofd > -1) && (ctrlfd > -1))
2585 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2586 else
2587 ast_waitstream(chan, ints);
2588 }
2589 ast_stopstream(chan);
2590}
2591
2592static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
2593{
2594 /* Initialise variables to allow compilation on Debian-stable, etc */
2595 int m1000E6 = 0;
2596 int i1000E6 = 0;
2597 int m1000E3 = 0;
2598 int i1000E3 = 0;
2599 int m1000 = 0;
2600 int i1000 = 0;
2601 int m100 = 0;
2602 int i100 = 0;
2603
2604 if (i == 0 && rzad > 0) {
2605 return;
2606 }
2607 if (i == 0) {
2608 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
2609 return;
2610 }
2611
2612 m1000E6 = i % 1000000000;
2613 i1000E6 = i / 1000000000;
2614
2615 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
2616
2617 m1000E3 = m1000E6 % 1000000;
2618 i1000E3 = m1000E6 / 1000000;
2619
2620 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
2621
2622 m1000 = m1000E3 % 1000;
2623 i1000 = m1000E3 / 1000;
2624
2625 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
2626
2627 m100 = m1000 % 100;
2628 i100 = m1000 / 100;
2629
2630 if (i100>0)
2631 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
2632
2633 if (m100 > 0 && m100 <= 9) {
2634 if (m1000 > 0)
2635 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
2636 else
2637 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
2638 } else if (m100 % 10 == 0 && m100 != 0) {
2639 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2640 } else if (m100 > 10 && m100 <= 19) {
2641 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
2642 } else if (m100 > 20) {
2643 if (odm->separator_dziesiatek[0] == ' ') {
2644 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2645 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
2646 } else {
2647 char buf[10];
2648 char *b = buf;
2649 b = pl_append(b, odm->dziesiatki[m100 / 10]);
2651 pl_append(b, odm->cyfry2[m100 % 10]);
2652 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
2653 }
2654 }
2655
2656 if (rzad > 0) {
2657 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
2658 }
2659}
2660
2661/* ast_say_number_full_pl: Polish syntax
2662
2663Sounds needed:
26640 zero
26651 jeden
266610 dziesiec
2667100 sto
26681000 tysiac
26691000000 milion
26701000000000 miliard
26711000000000.2 miliardy
26721000000000.5 miliardow
26731000000.2 miliony
26741000000.5 milionow
26751000.2 tysiace
26761000.5 tysiecy
2677100m stu
267810m dziesieciu
267911 jedenascie
268011m jedenastu
268112 dwanascie
268212m dwunastu
268313 trzynascie
268413m trzynastu
268514 czternascie
268614m czternastu
268715 pietnascie
268815m pietnastu
268916 szesnascie
269016m szesnastu
269117 siedemnascie
269217m siedemnastu
269318 osiemnascie
269418m osiemnastu
269519 dziewietnascie
269619m dziewietnastu
26971z jedna
26982 dwa
269920 dwadziescia
2700200 dwiescie
2701200m dwustu
270220m dwudziestu
27032-1m dwaj
27042-2m dwoch
27052z dwie
27063 trzy
270730 trzydziesci
2708300 trzysta
2709300m trzystu
271030m trzydziestu
27113-1m trzej
27123-2m trzech
27134 cztery
271440 czterdziesci
2715400 czterysta
2716400m czterystu
271740m czterdziestu
27184-1m czterej
27194-2m czterech
27205 piec
272150 piecdziesiat
2722500 piecset
2723500m pieciuset
272450m piedziesieciu
27255m pieciu
27266 szesc
272760 szescdziesiat
2728600 szescset
2729600m szesciuset
273060m szescdziesieciu
27316m szesciu
27327 siedem
273370 siedemdziesiat
2734700 siedemset
2735700m siedmiuset
273670m siedemdziesieciu
27377m siedmiu
27388 osiem
273980 osiemdziesiat
2740800 osiemset
2741800m osmiuset
274280m osiemdziesieciu
27438m osmiu
27449 dziewiec
274590 dziewiecdziesiat
2746900 dziewiecset
2747900m dziewieciuset
274890m dziewiedziesieciu
27499m dziewieciu
2750and combinations of eg.: 20_1, 30m_3m, etc...
2751
2752*/
2753static 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)
2754{
2755 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2756
2757 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2758
2759 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2760
2761 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2762
2763 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2764
2765 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2766
2767 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2768
2769 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2770
2771 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2772
2773 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2774
2775 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2776
2777 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2778
2779 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2780
2781 /* Initialise variables to allow compilation on Debian-stable, etc */
2782 odmiana *o;
2783
2784 static odmiana *odmiana_nieosobowa = NULL;
2785 static odmiana *odmiana_meska = NULL;
2786 static odmiana *odmiana_zenska = NULL;
2787
2788 if (odmiana_nieosobowa == NULL) {
2789 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2790
2791 odmiana_nieosobowa->separator_dziesiatek = " ";
2792
2793 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2794 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2795 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2796 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2797 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2798 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2799 }
2800
2801 if (odmiana_zenska == NULL) {
2802 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2803
2804 odmiana_zenska->separator_dziesiatek = " ";
2805
2806 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2807 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2808 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2809 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2810 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2811 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2812 }
2813
2814 if (odmiana_meska == NULL) {
2815 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2816
2817 odmiana_meska->separator_dziesiatek = " ";
2818
2819 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2820 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2821 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2822 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2823 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2824 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2825 }
2826
2827 if (options) {
2828 if (strncasecmp(options, "f", 1) == 0)
2829 o = odmiana_zenska;
2830 else if (strncasecmp(options, "m", 1) == 0)
2831 o = odmiana_meska;
2832 else
2833 o = odmiana_nieosobowa;
2834 } else
2835 o = odmiana_nieosobowa;
2836
2837 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2838 return 0;
2839}
2840
2841/* ast_say_number_full_pt: Portuguese syntax
2842
2843 * Extra sounds needed:
2844 * For feminin all sound files ends with F
2845 * 100E for 100+ something
2846 * 1000000S for plural
2847 * pt-e for 'and'
2848 */
2849static 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)
2850{
2851 int res = 0;
2852 int playh = 0;
2853 int mf = 1; /* +1 = male; -1 = female */
2854 char fn[256] = "";
2855
2856 if (!num)
2857 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2858
2859 if (options && !strncasecmp(options, "f", 1))
2860 mf = -1;
2861
2862 while (!res && num ) {
2863 if (num < 0) {
2864 ast_copy_string(fn, "digits/minus", sizeof(fn));
2865 if ( num > INT_MIN ) {
2866 num = -num;
2867 } else {
2868 num = 0;
2869 }
2870 } else if (num < 20) {
2871 if ((num == 1 || num == 2) && (mf < 0))
2872 snprintf(fn, sizeof(fn), "digits/%dF", num);
2873 else
2874 snprintf(fn, sizeof(fn), "digits/%d", num);
2875 num = 0;
2876 } else if (num < 100) {
2877 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2878 if (num % 10)
2879 playh = 1;
2880 num = num % 10;
2881 } else if (num < 1000) {
2882 if (num == 100)
2883 ast_copy_string(fn, "digits/100", sizeof(fn));
2884 else if (num < 200)
2885 ast_copy_string(fn, "digits/100E", sizeof(fn));
2886 else {
2887 if (mf < 0 && num > 199)
2888 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2889 else
2890 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2891 if (num % 100)
2892 playh = 1;
2893 }
2894 num = num % 100;
2895 } else if (num < 1000000) {
2896 if (num > 1999) {
2897 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2898 if (res)
2899 return res;
2900 }
2901 ast_copy_string(fn, "digits/1000", sizeof(fn));
2902 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2903 playh = 1;
2904 num = num % 1000;
2905 } else if (num < 1000000000) {
2906 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2907 if (res)
2908 return res;
2909 if (num < 2000000)
2910 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2911 else
2912 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2913
2914 if ((num % 1000000) &&
2915 /* no thousands */
2916 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2917 /* no hundreds and below */
2918 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2919 playh = 1;
2920 num = num % 1000000;
2921 } else {
2922 /* number is too big */
2923 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2924 res = -1;
2925 }
2926 if (!res) {
2927 if (!ast_streamfile(chan, fn, language)) {
2928 if ((audiofd > -1) && (ctrlfd > -1))
2929 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2930 else
2931 res = ast_waitstream(chan, ints);
2932 }
2933 ast_stopstream(chan);
2934 }
2935 if (!res && playh) {
2936 res = wait_file(chan, ints, "digits/pt-e", language);
2937 ast_stopstream(chan);
2938 playh = 0;
2939 }
2940 }
2941 return res;
2942}
2943
2944/*! \brief ast_say_number_full_se: Swedish syntax
2945
2946 Sound files needed
2947 - 1N
2948*/
2949static 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)
2950{
2951 int playh = 0;
2952 int start = 1;
2953 char fn[256] = "";
2954 int cn = 1; /* +1 = commune; -1 = neuter */
2955 int res = 0;
2956
2957 if (!num) {
2958 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2959 }
2960 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2961
2962 while (num || playh) {
2963 if (num < 0) {
2964 ast_copy_string(fn, "digits/minus", sizeof(fn));
2965 if ( num > INT_MIN ) {
2966 num = -num;
2967 } else {
2968 num = 0;
2969 }
2970 } else if (playh) {
2971 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2972 playh = 0;
2973 } else if (start && num < 200 && num > 99 && cn == -1) {
2974 /* Don't say "en hundra" just say "hundra". */
2975 snprintf(fn, sizeof(fn), "digits/hundred");
2976 num -= 100;
2977 } else if (num == 1 && cn == -1) { /* En eller ett? */
2978 ast_copy_string(fn, "digits/1N", sizeof(fn));
2979 num = 0;
2980 } else if (num < 20) {
2981 snprintf(fn, sizeof(fn), "digits/%d", num);
2982 num = 0;
2983 } else if (num < 100) { /* Below hundreds - teens and tens */
2984 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2985 num %= 10;
2986 } else if (num < 1000) {
2987 /* Hundreds */
2988 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2989 playh++;
2990 num %= 100;
2991 } else if (num < 1000000) { /* 1,000,000 */
2992 /* Always say "ett hundra tusen", not "en hundra tusen" */
2993 res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2994 if (res) {
2995 return res;
2996 }
2997 num %= 1000;
2998 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2999 } else if (num < 1000000000) { /* 1,000,000,000 */
3000 /* Always say "en miljon", not "ett miljon" */
3001 res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
3002 if (res) {
3003 return res;
3004 }
3005 num %= 1000000;
3006 ast_copy_string(fn, "digits/million", sizeof(fn));
3007 } else { /* Miljarder - Billions */
3008 ast_debug(1, "Number '%d' is too big for me\n", num);
3009 return -1;
3010 }
3011
3012 if (!ast_streamfile(chan, fn, language)) {
3013 if ((audiofd > -1) && (ctrlfd > -1)) {
3014 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3015 } else {
3016 res = ast_waitstream(chan, ints);
3017 }
3018 ast_stopstream(chan);
3019 if (res) {
3020 return res;
3021 }
3022 }
3023 start = 0;
3024 }
3025 return 0;
3026}
3027
3028/*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
3029static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3030{
3031 int res = 0;
3032 int playh = 0;
3033 int playt = 0;
3034 int playz = 0;
3035 int last_length = 0;
3036 char buf[20] = "";
3037 char fn[256] = "";
3038 if (!num)
3039 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3040
3041 while (!res && (num || playh || playt || playz)) {
3042 if (num < 0) {
3043 ast_copy_string(fn, "digits/minus", sizeof(fn));
3044 if ( num > INT_MIN ) {
3045 num = -num;
3046 } else {
3047 num = 0;
3048 }
3049 } else if (playz) {
3050 snprintf(fn, sizeof(fn), "digits/0");
3051 last_length = 0;
3052 playz = 0;
3053 } else if (playh) {
3054 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3055 playh = 0;
3056 } else if (playt) {
3057 snprintf(fn, sizeof(fn), "digits/thousand");
3058 playt = 0;
3059 } else if (num < 10) {
3060 snprintf(buf, 12, "%d", num);
3061 if (last_length - strlen(buf) > 1 && last_length != 0) {
3062 last_length = strlen(buf);
3063 playz++;
3064 continue;
3065 }
3066 snprintf(fn, sizeof(fn), "digits/%d", num);
3067 num = 0;
3068 } else if (num < 100) {
3069 snprintf(buf, 10, "%d", num);
3070 if (last_length - strlen(buf) > 1 && last_length != 0) {
3071 last_length = strlen(buf);
3072 playz++;
3073 continue;
3074 }
3075 last_length = strlen(buf);
3076 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3077 num %= 10;
3078 } else {
3079 if (num < 1000){
3080 snprintf(buf, 10, "%d", num);
3081 if (last_length - strlen(buf) > 1 && last_length != 0) {
3082 last_length = strlen(buf);
3083 playz++;
3084 continue;
3085 }
3086 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
3087 playh++;
3088 snprintf(buf, 10, "%d", num);
3089 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
3090 last_length = strlen(buf);
3091 num -= ((num / 100) * 100);
3092 } else if (num < 10000){
3093 snprintf(buf, 10, "%d", num);
3094 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
3095 playt++;
3096 snprintf(buf, 10, "%d", num);
3097 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
3098 last_length = strlen(buf);
3099 num -= ((num / 1000) * 1000);
3100 } else if (num < 100000000) { /* 100,000,000 */
3101 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
3102 if (res)
3103 return res;
3104 snprintf(buf, 10, "%d", num);
3105 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
3106 num -= ((num / 10000) * 10000);
3107 last_length = strlen(buf);
3108 snprintf(fn, sizeof(fn), "digits/wan");
3109 } else {
3110 if (num < 1000000000) { /* 1,000,000,000 */
3111 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
3112 if (res)
3113 return res;
3114 snprintf(buf, 10, "%d", num);
3115 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
3116 last_length = strlen(buf);
3117 num -= ((num / 100000000) * 100000000);
3118 snprintf(fn, sizeof(fn), "digits/yi");
3119 } else {
3120 ast_debug(1, "Number '%d' is too big for me\n", num);
3121 res = -1;
3122 }
3123 }
3124 }
3125 if (!res) {
3126 if (!ast_streamfile(chan, fn, language)) {
3127 if ((audiofd > -1) && (ctrlfd > -1))
3128 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3129 else
3130 res = ast_waitstream(chan, ints);
3131 }
3132 ast_stopstream(chan);
3133 }
3134 }
3135 return res;
3136}
3137
3138/*!\internal
3139 * \brief Counting in Urdu, the national language of Pakistan
3140 * \since 1.8
3141 */
3142static 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)
3143{
3144 int res = 0;
3145 int playh = 0;
3146 char fn[256] = "";
3147
3148 if (!num) {
3149 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3150 }
3151
3152 while (!res && (num || playh)) {
3153 if (playh) {
3154 snprintf(fn, sizeof(fn), "digits/hundred");
3155 playh = 0;
3156 } else if (num < 100) {
3157 snprintf(fn, sizeof(fn), "digits/%d", num);
3158 num = 0;
3159 } else if (num < 1000) {
3160 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
3161 playh++;
3162 num -= ((num / 100) * 100);
3163 } else if (num < 100000) { /* 1,00,000 */
3164 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
3165 return res;
3166 }
3167 num = num % 1000;
3168 snprintf(fn, sizeof(fn), "digits/thousand");
3169 } else if (num < 10000000) { /* 1,00,00,000 */
3170 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
3171 return res;
3172 }
3173 num = num % 100000;
3174 snprintf(fn, sizeof(fn), "digits/lac");
3175 } else if (num < 1000000000) { /* 1,00,00,00,000 */
3176 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
3177 return res;
3178 }
3179 num = num % 10000000;
3180 snprintf(fn, sizeof(fn), "digits/crore");
3181 } else {
3182 ast_debug(1, "Number '%d' is too big for me\n", num);
3183 res = -1;
3184 }
3185
3186 if (!res) {
3187 if (!ast_streamfile(chan, fn, language)) {
3188 if ((audiofd > -1) && (ctrlfd > -1)) {
3189 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3190 } else {
3191 res = ast_waitstream(chan, ints);
3192 }
3193 }
3194 ast_stopstream(chan);
3195 }
3196 }
3197 return res;
3198}
3199
3200/*! \brief determine last digits for thousands/millions (ru) */
3201static int get_lastdigits_ru(int num) {
3202 if (num < 20) {
3203 return num;
3204 } else if (num < 100) {
3205 return get_lastdigits_ru(num % 10);
3206 } else if (num < 1000) {
3207 return get_lastdigits_ru(num % 100);
3208 }
3209 return 0; /* number too big */
3210}
3211
3212
3213/*! \brief ast_say_number_full_ru: Russian syntax
3214
3215 additional files:
3216 n00.gsm (one hundred, two hundred, ...)
3217 thousand.gsm
3218 million.gsm
3219 thousands-i.gsm (tisyachi)
3220 million-a.gsm (milliona)
3221 thousands.gsm
3222 millions.gsm
3223 1f.gsm (odna)
3224 2f.gsm (dve)
3225
3226 where 'n' from 1 to 9
3227*/
3228static 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)
3229{
3230 int res = 0;
3231 int lastdigits = 0;
3232 char fn[256] = "";
3233 if (!num)
3234 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3235
3236 while (!res && (num)) {
3237 if (num < 0) {
3238 ast_copy_string(fn, "digits/minus", sizeof(fn));
3239 if ( num > INT_MIN ) {
3240 num = -num;
3241 } else {
3242 num = 0;
3243 }
3244 } else if (num < 20) {
3245 if (options && strlen(options) == 1 && num < 3) {
3246 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
3247 } else {
3248 snprintf(fn, sizeof(fn), "digits/%d", num);
3249 }
3250 num = 0;
3251 } else if (num < 100) {
3252 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
3253 num %= 10;
3254 } else if (num < 1000){
3255 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
3256 num %= 100;
3257 } else if (num < 1000000) { /* 1,000,000 */
3258 lastdigits = get_lastdigits_ru(num / 1000);
3259 /* say thousands */
3260 if (lastdigits < 3) {
3261 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
3262 } else {
3263 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
3264 }
3265 if (res)
3266 return res;
3267 if (lastdigits == 1) {
3268 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3269 } else if (lastdigits > 1 && lastdigits < 5) {
3270 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
3271 } else {
3272 ast_copy_string(fn, "digits/thousands", sizeof(fn));
3273 }
3274 num %= 1000;
3275 } else if (num < 1000000000) { /* 1,000,000,000 */
3276 lastdigits = get_lastdigits_ru(num / 1000000);
3277 /* say millions */
3278 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
3279 if (res)
3280 return res;
3281 if (lastdigits == 1) {
3282 ast_copy_string(fn, "digits/million", sizeof(fn));
3283 } else if (lastdigits > 1 && lastdigits < 5) {
3284 ast_copy_string(fn, "digits/million-a", sizeof(fn));
3285 } else {
3286 ast_copy_string(fn, "digits/millions", sizeof(fn));
3287 }
3288 num %= 1000000;
3289 } else {
3290 ast_debug(1, "Number '%d' is too big for me\n", num);
3291 res = -1;
3292 }
3293 if (!res) {
3294 if (!ast_streamfile(chan, fn, language)) {
3295 if ((audiofd > -1) && (ctrlfd > -1))
3296 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3297 else
3298 res = ast_waitstream(chan, ints);
3299 }
3300 ast_stopstream(chan);
3301 }
3302 }
3303 return res;
3304}
3305
3306/*! \brief Thai syntax */
3307static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3308{
3309 int res = 0;
3310 int playh = 0;
3311 char fn[256] = "";
3312 if (!num)
3313 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3314
3315 while(!res && (num || playh)) {
3316 if (num < 0) {
3317 ast_copy_string(fn, "digits/lop", sizeof(fn));
3318 if ( num > INT_MIN ) {
3319 num = -num;
3320 } else {
3321 num = 0;
3322 }
3323 } else if (playh) {
3324 ast_copy_string(fn, "digits/roi", sizeof(fn));
3325 playh = 0;
3326 } else if (num < 100) {
3327 if ((num <= 20) || ((num % 10) == 1)) {
3328 snprintf(fn, sizeof(fn), "digits/%d", num);
3329 num = 0;
3330 } else {
3331 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3332 num %= 10;
3333 }
3334 } else if (num < 1000) {
3335 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3336 playh++;
3337 num %= 100;
3338 } else if (num < 10000) { /* 10,000 */
3339 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
3340 if (res)
3341 return res;
3342 num %= 1000;
3343 ast_copy_string(fn, "digits/pan", sizeof(fn));
3344 } else if (num < 100000) { /* 100,000 */
3345 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
3346 if (res)
3347 return res;
3348 num %= 10000;
3349 ast_copy_string(fn, "digits/muan", sizeof(fn));
3350 } else if (num < 1000000) { /* 1,000,000 */
3351 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
3352 if (res)
3353 return res;
3354 num %= 100000;
3355 ast_copy_string(fn, "digits/san", sizeof(fn));
3356 } else {
3357 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3358 if (res)
3359 return res;
3360 num %= 1000000;
3361 ast_copy_string(fn, "digits/larn", sizeof(fn));
3362 }
3363 if (!res) {
3364 if(!ast_streamfile(chan, fn, language)) {
3365 if ((audiofd > -1) && (ctrlfd > -1))
3366 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3367 else
3368 res = ast_waitstream(chan, ints);
3369 }
3370 ast_stopstream(chan);
3371 }
3372 }
3373 return res;
3374}
3375
3376/*! \brief ast_say_number_full_vi: Vietnamese syntax */
3377static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3378{
3379 int res = 0;
3380 int playh = 0;
3381 int playoh = 0;
3382 int playohz = 0;
3383 int playz = 0;
3384 int playl = 0;
3385 char fn[256] = "";
3386 if (!num)
3387 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3388 while (!res && (num || playh)) {
3389 if (num < 0) {
3390 ast_copy_string(fn, "digits/minus", sizeof(fn));
3391 if ( num > INT_MIN ) {
3392 num = -num;
3393 } else {
3394 num = 0;
3395 }
3396 } else if (playl) {
3397 snprintf(fn, sizeof(fn), "digits/%da", num);
3398 playl = 0;
3399 num = 0;
3400 } else if (playh) {
3401 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3402 playh = 0;
3403 } else if (playz) {
3404 ast_copy_string(fn, "digits/odd", sizeof(fn));
3405 playz = 0;
3406 } else if (playoh) {
3407 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
3408 playoh = 0;
3409 } else if (playohz) {
3410 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
3411 playohz = 0;
3412 } else if (num < 20) {
3413 snprintf(fn, sizeof(fn), "digits/%d", num);
3414 num = 0;
3415 } else if (num < 100) {
3416 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
3417 num %= 10;
3418 if ((num == 5) || (num == 4) || (num == 1)) playl++;
3419 } else {
3420 if (num < 1000) {
3421 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3422 num %= 100;
3423 if (num && (num < 10)) {
3424 playz++;
3425 playh++;
3426 } else {
3427 playh++;
3428 }
3429 } else {
3430 if (num < 1000000) { /* 1,000,000 */
3431 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
3432 if (res)
3433 return res;
3434 num %= 1000;
3435 snprintf(fn, sizeof(fn), "digits/thousand");
3436 if (num && (num < 10)) {
3437 playohz++;
3438 } else if (num && (num < 100)){
3439 playoh++;
3440 } else {
3441 playh = 0;
3442 playohz = 0;
3443 playoh = 0;
3444 }
3445 } else {
3446 if (num < 1000000000) { /* 1,000,000,000 */
3447 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3448 if (res)
3449 return res;
3450 num %= 1000000;
3451 ast_copy_string(fn, "digits/million", sizeof(fn));
3452 } else {
3453 res = -1;
3454 }
3455 }
3456 }
3457 }
3458 if (!res) {
3459 if (!ast_streamfile(chan, fn, language)) {
3460 if ((audiofd > -1) && (ctrlfd > -1))
3461 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3462 else
3463 res = ast_waitstream(chan, ints);
3464 }
3465 ast_stopstream(chan);
3466 }
3467 }
3468 return res;
3469}
3470
3471/*! \brief ast_say_enumeration_full: call language-specific functions
3472 * \note Called from AGI */
3473static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3474{
3475 if (!strncasecmp(language, "en", 2)) { /* English syntax */
3476 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3477 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
3478 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
3479 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
3480 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
3481 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
3482 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
3483 } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
3484 return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
3485 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
3486 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3487 } else if (!strncasecmp(language, "ps", 2)) { /* Pashto syntax */
3488 return ast_say_enumeration_full_ps(chan, num, ints, language, audiofd, ctrlfd);
3489 } else if (!strncasecmp(language, "dr", 2)) { /* Dari syntax */
3490 return ast_say_enumeration_full_dr(chan, num, ints, language, audiofd, ctrlfd);
3491 }
3492
3493 /* Default to english */
3494 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3495}
3496
3497/*! \brief ast_say_enumeration_full_en: English syntax
3498 \note This is the default syntax, if no other syntax defined in this file is used */
3499static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3500{
3501 int res = 0, t = 0;
3502 char fn[256] = "";
3503
3504 while (!res && num) {
3505 if (num < 0) {
3506 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3507 if ( num > INT_MIN ) {
3508 num = -num;
3509 } else {
3510 num = 0;
3511 }
3512 } else if (num < 20) {
3513 snprintf(fn, sizeof(fn), "digits/h-%d", num);
3514 num = 0;
3515 } else if (num < 100) {
3516 int tens = num / 10;
3517 num = num % 10;
3518 if (num == 0) {
3519 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3520 } else {
3521 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3522 }
3523 } else if (num < 1000) {
3524 int hundreds = num / 100;
3525 num = num % 100;
3526 if (hundreds > 1 || t == 1) {
3527 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3528 }
3529 if (res)
3530 return res;
3531 if (num) {
3532 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3533 } else {
3534 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3535 }
3536 } else if (num < 1000000) {
3537 int thousands = num / 1000;
3538 num = num % 1000;
3539 if (thousands > 1 || t == 1) {
3540 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3541 }
3542 if (res)
3543 return res;
3544 if (num) {
3545 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3546 } else {
3547 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3548 }
3549 t = 1;
3550 } else if (num < 1000000000) {
3551 int millions = num / 1000000;
3552 num = num % 1000000;
3553 t = 1;
3554 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3555 if (res)
3556 return res;
3557 if (num) {
3558 ast_copy_string(fn, "digits/million", sizeof(fn));
3559 } else {
3560 ast_copy_string(fn, "digits/h-million", sizeof(fn));
3561 }
3562 } else if (num < INT_MAX) {
3563 int billions = num / 1000000000;
3564 num = num % 1000000000;
3565 t = 1;
3566 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3567 if (res)
3568 return res;
3569 if (num) {
3570 ast_copy_string(fn, "digits/billion", sizeof(fn));
3571 } else {
3572 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3573 }
3574 } else if (num == INT_MAX) {
3575 ast_copy_string(fn, "digits/h-last", sizeof(fn));
3576 num = 0;
3577 } else {
3578 ast_debug(1, "Number '%d' is too big for me\n", num);
3579 res = -1;
3580 }
3581
3582 if (!res) {
3583 if (!ast_streamfile(chan, fn, language)) {
3584 if ((audiofd > -1) && (ctrlfd > -1)) {
3585 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3586 } else {
3587 res = ast_waitstream(chan, ints);
3588 }
3589 }
3590 ast_stopstream(chan);
3591 }
3592 }
3593 return res;
3594}
3595
3596/*! \brief ast_say_enumeration_full_ps: Pashto syntax
3597 \note This is the default syntax, if no other syntax defined in this file is used */
3598static int ast_say_enumeration_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3599{
3600 int res = 0, t = 0;
3601 char fn[256] = "";
3602
3603 while (!res && num) {
3604 if (num < 0) {
3605 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3606 if ( num > INT_MIN ) {
3607 num = -num;
3608 } else {
3609 num = 0;
3610 }
3611 } else if (num <= 31) {
3612 snprintf(fn, sizeof(fn), "digits/h-%d", num);
3613 num = 0;
3614 } else if (num < 100) {
3615 int tens = num / 10;
3616 num = num % 10;
3617 if (num == 0) {
3618 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3619 } else {
3620 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3621 }
3622 } else if (num < 1000) {
3623 int hundreds = num / 100;
3624 num = num % 100;
3625 if (hundreds > 1 || t == 1) {
3626 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3627 }
3628 if (res)
3629 return res;
3630 if (num) {
3631 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3632 } else {
3633 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3634 }
3635 } else if (num < 1000000) {
3636 int thousands = num / 1000;
3637 num = num % 1000;
3638 if (thousands > 1 || t == 1) {
3639 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3640 }
3641 if (res)
3642 return res;
3643 if (num) {
3644 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3645 } else {
3646 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3647 }
3648 t = 1;
3649 } else if (num < 1000000000) {
3650 int millions = num / 1000000;
3651 num = num % 1000000;
3652 t = 1;
3653 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3654 if (res)
3655 return res;
3656 if (num) {
3657 ast_copy_string(fn, "digits/million", sizeof(fn));
3658 } else {
3659 ast_copy_string(fn, "digits/h-million", sizeof(fn));
3660 }
3661 } else if (num < INT_MAX) {
3662 int billions = num / 1000000000;
3663 num = num % 1000000000;
3664 t = 1;
3665 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3666 if (res)
3667 return res;
3668 if (num) {
3669 ast_copy_string(fn, "digits/billion", sizeof(fn));
3670 } else {
3671 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3672 }
3673 } else if (num == INT_MAX) {
3674 ast_copy_string(fn, "digits/h-last", sizeof(fn));
3675 num = 0;
3676 } else {
3677 ast_debug(1, "Number '%d' is too big for me\n", num);
3678 res = -1;
3679 }
3680
3681 if (!res) {
3682 if (!ast_streamfile(chan, fn, language)) {
3683 if ((audiofd > -1) && (ctrlfd > -1)) {
3684 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3685 } else {
3686 res = ast_waitstream(chan, ints);
3687 }
3688 }
3689 ast_stopstream(chan);
3690 }
3691 }
3692 return res;
3693}
3694
3695/*! \brief ast_say_enumeration_full_dr: Dari syntax
3696 \note This is the default syntax, if no other syntax defined in this file is used */
3697static int ast_say_enumeration_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3698{
3699 int res = 0, t = 0;
3700 char fn[256] = "";
3701
3702 while (!res && num) {
3703 if (num < 0) {
3704 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3705 if ( num > INT_MIN ) {
3706 num = -num;
3707 } else {
3708 num = 0;
3709 }
3710 } else if (num <= 31) {
3711 snprintf(fn, sizeof(fn), "digits/h-%d", num);
3712 num = 0;
3713 } else if (num < 100) {
3714 int tens = num / 10;
3715 num = num % 10;
3716 if (num == 0) {
3717 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3718 } else {
3719 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3720 }
3721 } else if (num < 1000) {
3722 int hundreds = num / 100;
3723 num = num % 100;
3724 if (hundreds > 1 || t == 1) {
3725 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3726 }
3727 if (res)
3728 return res;
3729 if (num) {
3730 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3731 } else {
3732 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3733 }
3734 } else if (num < 1000000) {
3735 int thousands = num / 1000;
3736 num = num % 1000;
3737 if (thousands > 1 || t == 1) {
3738 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3739 }
3740 if (res)
3741 return res;
3742 if (num) {
3743 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3744 } else {
3745 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3746 }
3747 t = 1;
3748 } else if (num < 1000000000) {
3749 int millions = num / 1000000;
3750 num = num % 1000000;
3751 t = 1;
3752 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3753 if (res)
3754 return res;
3755 if (num) {
3756 ast_copy_string(fn, "digits/million", sizeof(fn));
3757 } else {
3758 ast_copy_string(fn, "digits/h-million", sizeof(fn));
3759 }
3760 } else if (num < INT_MAX) {
3761 int billions = num / 1000000000;
3762 num = num % 1000000000;
3763 t = 1;
3764 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3765 if (res)
3766 return res;
3767 if (num) {
3768 ast_copy_string(fn, "digits/billion", sizeof(fn));
3769 } else {
3770 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3771 }
3772 } else if (num == INT_MAX) {
3773 ast_copy_string(fn, "digits/h-last", sizeof(fn));
3774 num = 0;
3775 } else {
3776 ast_debug(1, "Number '%d' is too big for me\n", num);
3777 res = -1;
3778 }
3779
3780 if (!res) {
3781 if (!ast_streamfile(chan, fn, language)) {
3782 if ((audiofd > -1) && (ctrlfd > -1)) {
3783 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3784 } else {
3785 res = ast_waitstream(chan, ints);
3786 }
3787 }
3788 ast_stopstream(chan);
3789 }
3790 }
3791 return res;
3792}
3793
3794static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3795{
3796 int res = 0;
3797 char fn[256] = "";
3798 ast_copy_string(fn, "digits/h", sizeof(fn));
3799 if (!res) {
3800 if (!ast_streamfile(chan, fn, language)) {
3801 if ((audiofd > -1) && (ctrlfd > -1)) {
3802 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3803 } else {
3804 res = ast_waitstream(chan, ints);
3805 }
3806 }
3807 ast_stopstream(chan);
3808 }
3809
3810 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3811}
3812
3813/*! \brief ast_say_enumeration_full_da: Danish syntax */
3814static 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)
3815{
3816 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3817 int res = 0, t = 0;
3818 char fn[256] = "", fna[256] = "";
3819 char *gender;
3820
3821 if (options && !strncasecmp(options, "f", 1)) {
3822 gender = "F";
3823 } else if (options && !strncasecmp(options, "n", 1)) {
3824 gender = "N";
3825 } else {
3826 gender = "";
3827 }
3828
3829 if (!num)
3830 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3831
3832 while (!res && num) {
3833 if (num < 0) {
3834 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3835 if ( num > INT_MIN ) {
3836 num = -num;
3837 } else {
3838 num = 0;
3839 }
3840 } else if (num < 100 && t) {
3841 ast_copy_string(fn, find_first_and(language), sizeof(fn));
3842 t = 0;
3843 } else if (num < 20) {
3844 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3845 num = 0;
3846 } else if (num < 100) {
3847 int ones = num % 10;
3848 if (ones) {
3849 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3850 num -= ones;
3851 } else {
3852 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3853 num = 0;
3854 }
3855 } else if (num == 100 && t == 0) {
3856 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3857 num = 0;
3858 } else if (num < 1000) {
3859 int hundreds = num / 100;
3860 num = num % 100;
3861 if (hundreds == 1) {
3862 ast_copy_string(fn, "digits/1N", sizeof(fn));
3863 } else {
3864 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3865 }
3866 if (num) {
3867 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3868 } else {
3869 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3870 }
3871 t = 1;
3872 } else if (num < 1000000) {
3873 int thousands = num / 1000;
3874 num = num % 1000;
3875 if (thousands == 1) {
3876 if (num) {
3877 ast_copy_string(fn, "digits/1N", sizeof(fn));
3878 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3879 } else {
3880 if (t) {
3881 ast_copy_string(fn, "digits/1N", sizeof(fn));
3882 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3883 } else {
3884 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3885 }
3886 }
3887 } else {
3888 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3889 if (res) {
3890 return res;
3891 }
3892 if (num) {
3893 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3894 } else {
3895 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3896 }
3897 }
3898 t = 1;
3899 } else if (num < 1000000000) {
3900 int millions = num / 1000000;
3901 num = num % 1000000;
3902 if (millions == 1) {
3903 if (num) {
3904 ast_copy_string(fn, "digits/1F", sizeof(fn));
3905 ast_copy_string(fna, "digits/million", sizeof(fna));
3906 } else {
3907 ast_copy_string(fn, "digits/1N", sizeof(fn));
3908 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3909 }
3910 } else {
3911 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3912 if (res) {
3913 return res;
3914 }
3915 if (num) {
3916 ast_copy_string(fn, "digits/millions", sizeof(fn));
3917 } else {
3918 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3919 }
3920 }
3921 t = 1;
3922 } else if (num < INT_MAX) {
3923 int billions = num / 1000000000;
3924 num = num % 1000000000;
3925 if (billions == 1) {
3926 if (num) {
3927 ast_copy_string(fn, "digits/1F", sizeof(fn));
3928 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3929 } else {
3930 ast_copy_string(fn, "digits/1N", sizeof(fn));
3931 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3932 }
3933 } else {
3934 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3935 if (res)
3936 return res;
3937 if (num) {
3938 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3939 } else {
3940 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3941 }
3942 }
3943 t = 1;
3944 } else if (num == INT_MAX) {
3945 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3946 num = 0;
3947 } else {
3948 ast_debug(1, "Number '%d' is too big for me\n", num);
3949 res = -1;
3950 }
3951
3952 if (!res) {
3953 if (!ast_streamfile(chan, fn, language)) {
3954 if ((audiofd > -1) && (ctrlfd > -1))
3955 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3956 else
3957 res = ast_waitstream(chan, ints);
3958 }
3959 ast_stopstream(chan);
3960 if (!res) {
3961 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3962 if ((audiofd > -1) && (ctrlfd > -1)) {
3963 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3964 } else {
3965 res = ast_waitstream(chan, ints);
3966 }
3967 }
3968 ast_stopstream(chan);
3969 strcpy(fna, "");
3970 }
3971 }
3972 }
3973 return res;
3974}
3975
3976/*! \brief ast_say_enumeration_full_de: German syntax */
3977static 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)
3978{
3979 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3980 int res = 0, t = 0;
3981 char fn[256] = "", fna[256] = "";
3982 char *gender;
3983
3984 if (options && !strncasecmp(options, "f", 1)) {
3985 gender = "F";
3986 } else if (options && !strncasecmp(options, "n", 1)) {
3987 gender = "N";
3988 } else {
3989 gender = "";
3990 }
3991
3992 if (!num)
3993 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3994
3995 while (!res && num) {
3996 if (num < 0) {
3997 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3998 if ( num > INT_MIN ) {
3999 num = -num;
4000 } else {
4001 num = 0;
4002 }
4003 } else if (num < 100 && t) {
4004 ast_copy_string(fn, find_first_and(language), sizeof(fn));
4005 t = 0;
4006 } else if (num < 20) {
4007 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
4008 num = 0;
4009 } else if (num < 100) {
4010 int ones = num % 10;
4011 if (ones) {
4012 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
4013 num -= ones;
4014 } else {
4015 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
4016 num = 0;
4017 }
4018 } else if (num == 100 && t == 0) {
4019 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
4020 num = 0;
4021 } else if (num < 1000) {
4022 int hundreds = num / 100;
4023 num = num % 100;
4024 if (hundreds == 1) {
4025 ast_copy_string(fn, "digits/1N", sizeof(fn));
4026 } else {
4027 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
4028 }
4029 if (num) {
4030 ast_copy_string(fna, "digits/hundred", sizeof(fna));
4031 } else {
4032 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
4033 }
4034 t = 1;
4035 } else if (num < 1000000) {
4036 int thousands = num / 1000;
4037 num = num % 1000;
4038 if (thousands == 1) {
4039 if (num) {
4040 ast_copy_string(fn, "digits/1N", sizeof(fn));
4041 ast_copy_string(fna, "digits/thousand", sizeof(fna));
4042 } else {
4043 if (t) {
4044 ast_copy_string(fn, "digits/1N", sizeof(fn));
4045 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
4046 } else {
4047 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
4048 }
4049 }
4050 } else {
4051 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
4052 if (res) {
4053 return res;
4054 }
4055 if (num) {
4056 ast_copy_string(fn, "digits/thousand", sizeof(fn));
4057 } else {
4058 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
4059 }
4060 }
4061 t = 1;
4062 } else if (num < 1000000000) {
4063 int millions = num / 1000000;
4064 num = num % 1000000;
4065 if (millions == 1) {
4066 if (num) {
4067 ast_copy_string(fn, "digits/1F", sizeof(fn));
4068 ast_copy_string(fna, "digits/million", sizeof(fna));
4069 } else {
4070 ast_copy_string(fn, "digits/1N", sizeof(fn));
4071 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
4072 }
4073 } else {
4074 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
4075 if (res) {
4076 return res;
4077 }
4078 if (num) {
4079 ast_copy_string(fn, "digits/millions", sizeof(fn));
4080 } else {
4081 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
4082 }
4083 }
4084 t = 1;
4085 } else if (num < INT_MAX) {
4086 int billions = num / 1000000000;
4087 num = num % 1000000000;
4088 if (billions == 1) {
4089 if (num) {
4090 ast_copy_string(fn, "digits/1F", sizeof(fn));
4091 ast_copy_string(fna, "digits/milliard", sizeof(fna));
4092 } else {
4093 ast_copy_string(fn, "digits/1N", sizeof(fn));
4094 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
4095 }
4096 } else {
4097 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
4098 if (res)
4099 return res;
4100 if (num) {
4101 ast_copy_string(fn, "digits/milliards", sizeof(fna));
4102 } else {
4103 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
4104 }
4105 }
4106 t = 1;
4107 } else if (num == INT_MAX) {
4108 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
4109 num = 0;
4110 } else {
4111 ast_debug(1, "Number '%d' is too big for me\n", num);
4112 res = -1;
4113 }
4114
4115 if (!res) {
4116 if (!ast_streamfile(chan, fn, language)) {
4117 if ((audiofd > -1) && (ctrlfd > -1))
4118 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
4119 else
4120 res = ast_waitstream(chan, ints);
4121 }
4122 ast_stopstream(chan);
4123 if (!res) {
4124 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
4125 if ((audiofd > -1) && (ctrlfd > -1)) {
4126 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
4127 } else {
4128 res = ast_waitstream(chan, ints);
4129 }
4130 }
4131 ast_stopstream(chan);
4132 strcpy(fna, "");
4133 }
4134 }
4135 }
4136 return res;
4137}
4138
4139static 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)
4140{
4141 int res = 0;
4142 char fn[256] = "";
4143 int mf = -1; /* +1 = Masculin; -1 = Feminin */
4144 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
4145
4146 if (options && !strncasecmp(options, "m", 1)) {
4147 mf = -1;
4148 }
4149
4150 ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
4151
4152 while (!res && num) {
4153 if (num < 0) {
4154 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
4155 if (num > INT_MIN) {
4156 num = -num;
4157 } else {
4158 num = 0;
4159 }
4160 } else if (num < 21) {
4161 if (mf < 0) {
4162 if (num < 10) {
4163 snprintf(fn, sizeof(fn), "digits/f-0%d", num);
4164 } else {
4165 snprintf(fn, sizeof(fn), "digits/f-%d", num);
4166 }
4167 } else {
4168 if (num < 10) {
4169 snprintf(fn, sizeof(fn), "digits/m-0%d", num);
4170 } else {
4171 snprintf(fn, sizeof(fn), "digits/m-%d", num);
4172 }
4173 }
4174 num = 0;
4175 } else if ((num < 100) && num >= 20) {
4176 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
4177 num = num % 10;
4178 } else if ((num >= 100) && (num < 1000)) {
4179 int tmpnum = num / 100;
4180 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
4181 num = num - (tmpnum * 100);
4182 } else if ((num >= 1000) && (num < 10000)) {
4183 int tmpnum = num / 1000;
4184 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
4185 num = num - (tmpnum * 1000);
4186 } else if (num < 20000) {
4187 snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
4188 num = num % 1000;
4189 } else if (num < 1000000) {
4190 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
4191 if (res) {
4192 return res;
4193 }
4194 snprintf(fn, sizeof(fn), "digits/1k");
4195 num = num % 1000;
4196 } else if (num < 2000000) {
4197 snprintf(fn, sizeof(fn), "digits/1m");
4198 num = num % 1000000;
4199 } else if (num < 3000000) {
4200 snprintf(fn, sizeof(fn), "digits/2m");
4201 num = num - 2000000;
4202 } else if (num < 1000000000) {
4203 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
4204 if (res) {
4205 return res;
4206 }
4207 snprintf(fn, sizeof(fn), "digits/1m");
4208 num = num % 1000000;
4209 } else {
4210 ast_debug(1, "Number '%d' is too big for me\n", num);
4211 res = -1;
4212 }
4213 if (!res) {
4214 if (!ast_streamfile(chan, fn, language)) {
4215 if ((audiofd > -1) && (ctrlfd > -1)) {
4216 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
4217 } else {
4218 res = ast_waitstream(chan, ints);
4219 }
4220 }
4221 ast_stopstream(chan);
4222 }
4223 }
4224 return res;
4225}
4226
4227/*! \brief ast_say_enumeration_full_is: Icelandic syntax */
4228static 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)
4229{
4230 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
4231 int res = 0, t = 0;
4232 char fn[256] = "", fna[256] = "";
4233 char *gender;
4234
4235 if (options && !strncasecmp(options, "f", 1)) {
4236 gender = "F";
4237 } else if (options && !strncasecmp(options, "n", 1)) {
4238 gender = "N";
4239 } else {
4240 gender = "";
4241 }
4242
4243 if (!num)
4244 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
4245
4246 while (!res && num) {
4247 if (num < 0) {
4248 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
4249 if ( num > INT_MIN ) {
4250 num = -num;
4251 } else {
4252 num = 0;
4253 }
4254 } else if (num < 100 && t) {
4255 ast_copy_string(fn, find_first_and(language), sizeof(fn));
4256 t = 0;
4257 } else if (num < 20) {
4258 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
4259 num = 0;
4260 } else if (num < 100) {
4261 int ones = num % 10;
4262 if (ones) {
4263 int tens = num - ones;
4264 snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender);
4265 num = ones;
4266 t++;
4267 }
4268 else if (t) {
4269 ast_copy_string(fn, find_first_and(language), sizeof(fn));
4270 t = 0;
4271 }
4272 else {
4273 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
4274 num = 0;
4275 }
4276
4277 } else if (num == 100 && t == 0) {
4278 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
4279 num = 0;
4280 } else if (num < 1000) {
4281 int hundreds = num / 100;
4282 num = num % 100;
4283 if (hundreds == 1) {
4284 ast_copy_string(fn, "digits/1hk", sizeof(fn));
4285 } else {
4286 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
4287 }
4288 if (num) {
4289 ast_copy_string(fna, "digits/hundred", sizeof(fna));
4290 } else {
4291 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
4292 }
4293 t = 1;
4294 } else if (num < 1000000) {
4295 int thousands = num / 1000;
4296 num = num % 1000;
4297 if (thousands == 1) {
4298 if (num) {
4299 /* Thousand is a neutral word, so use the neutral recording */
4300 ast_copy_string(fn, "digits/1hk", sizeof(fn));
4301 ast_copy_string(fna, "digits/thousand", sizeof(fna));
4302 } else {
4303 if (t) {
4304 ast_copy_string(fn, "digits/1hk", sizeof(fn));
4305 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
4306 } else {
4307 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
4308 }
4309 }
4310 } else {
4311 res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd);
4312 if (res) {
4313 return res;
4314 }
4315 if (num) {
4316 ast_copy_string(fn, "digits/thousand", sizeof(fn));
4317 } else {
4318 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
4319 }
4320 }
4321 if (num)
4322 t = 1;
4323 } else if (num < 1000000000) {
4324 int millions = num / 1000000;
4325 num = num % 1000000;
4326 if (millions == 1) {
4327 if (num) {
4328 /* Million is a feminine word, so use the female form */
4329 ast_copy_string(fn, "digits/1kvk", sizeof(fn));
4330 ast_copy_string(fna, "digits/million", sizeof(fna));
4331 } else {
4332 ast_copy_string(fn, "digits/1hk", sizeof(fn));
4333 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
4334 }
4335 } else {
4336 res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd);
4337 if (res) {
4338 return res;
4339 }
4340 if (num) {
4341 ast_copy_string(fn, "digits/millions", sizeof(fn));
4342 } else {
4343 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
4344 }
4345 }
4346 if (num)
4347 t = 1;
4348 } else if (num < INT_MAX) {
4349 int billions = num / 1000000000;
4350 num = num % 1000000000;
4351 if (billions == 1) {
4352 if (num) {
4353 ast_copy_string(fn, "digits/1", sizeof(fn));
4354 ast_copy_string(fna, "digits/milliard", sizeof(fna));
4355 } else {
4356 ast_copy_string(fn, "digits/1hk", sizeof(fn));
4357 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
4358 }
4359 } else {
4360 res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd);
4361 if (res)
4362 return res;
4363 if (num) {
4364 ast_copy_string(fn, "digits/milliards", sizeof(fna));
4365 } else {
4366 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
4367 }
4368 }
4369 if (num)
4370 t = 1;
4371 } else if (num == INT_MAX) {
4372 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
4373 num = 0;
4374 } else {
4375 ast_debug(1, "Number '%d' is too big for me\n", num);
4376 res = -1;
4377 }
4378
4379 if (!res) {
4380 if (!ast_streamfile(chan, fn, language)) {
4381 if ((audiofd > -1) && (ctrlfd > -1))
4382 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
4383 else
4384 res = ast_waitstream(chan, ints);
4385 }
4386 ast_stopstream(chan);
4387 if (!res) {
4388 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
4389 if ((audiofd > -1) && (ctrlfd > -1)) {
4390 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
4391 } else {
4392 res = ast_waitstream(chan, ints);
4393 }
4394 }
4395 ast_stopstream(chan);
4396 strcpy(fna, "");
4397 }
4398 }
4399 }
4400 return res;
4401}
4402
4403static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4404{
4405 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4406 return ast_say_date_en(chan, t, ints, lang);
4407 } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4408 return ast_say_date_da(chan, t, ints, lang);
4409 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4410 return ast_say_date_de(chan, t, ints, lang);
4411 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4412 return ast_say_date_fr(chan, t, ints, lang);
4413 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4414 return ast_say_date_gr(chan, t, ints, lang);
4415 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4416 return ast_say_date_ja(chan, t, ints, lang);
4417 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4418 return ast_say_date_he(chan, t, ints, lang);
4419 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
4420 return ast_say_date_hu(chan, t, ints, lang);
4421 } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4422 return ast_say_date_is(chan, t, ints, lang);
4423 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
4424 return ast_say_date_ka(chan, t, ints, lang);
4425 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4426 return ast_say_date_nl(chan, t, ints, lang);
4427 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4428 return ast_say_date_pt(chan, t, ints, lang);
4429 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4430 return ast_say_date_th(chan, t, ints, lang);
4431 } else if (!strncasecmp(lang, "ps", 2)) { /* Pashto syntax */
4432 return ast_say_date_en(chan, t, ints, lang);
4433 } else if (!strncasecmp(lang, "dr", 2)) { /* Dari syntax */
4434 return ast_say_date_en(chan, t, ints, lang);
4435 }
4436
4437 /* Default to English */
4438 return ast_say_date_en(chan, t, ints, lang);
4439}
4440
4441/*! \brief English syntax */
4442int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4443{
4444 struct ast_tm tm;
4445 struct timeval when = { t, 0 };
4446 char fn[256];
4447 int res = 0;
4448 ast_localtime(&when, &tm, NULL);
4449 if (!res) {
4450 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4451 res = ast_streamfile(chan, fn, lang);
4452 if (!res)
4453 res = ast_waitstream(chan, ints);
4454 }
4455 if (!res) {
4456 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4457 res = ast_streamfile(chan, fn, lang);
4458 if (!res)
4459 res = ast_waitstream(chan, ints);
4460 }
4461 if (!res)
4462 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4463 if (!res)
4464 res = ast_waitstream(chan, ints);
4465 if (!res)
4466 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4467 return res;
4468}
4469
4470/*! \brief Danish syntax */
4471int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4472{
4473 struct timeval when = { t, 0 };
4474 struct ast_tm tm;
4475 char fn[256];
4476 int res = 0;
4477 ast_localtime(&when, &tm, NULL);
4478 if (!res) {
4479 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4480 res = ast_streamfile(chan, fn, lang);
4481 if (!res)
4482 res = ast_waitstream(chan, ints);
4483 }
4484 if (!res)
4485 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4486 if (!res)
4487 res = ast_waitstream(chan, ints);
4488 if (!res) {
4489 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4490 res = ast_streamfile(chan, fn, lang);
4491 if (!res)
4492 res = ast_waitstream(chan, ints);
4493 }
4494 if (!res) {
4495 /* Year */
4496 int year = tm.tm_year + 1900;
4497 if (year > 1999) { /* year 2000 and later */
4498 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4499 } else {
4500 if (year < 1100) {
4501 /* I'm not going to handle 1100 and prior */
4502 /* We'll just be silent on the year, instead of bombing out. */
4503 } else {
4504 /* year 1100 to 1999. will anybody need this?!? */
4505 snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4506 res = wait_file(chan, ints, fn, lang);
4507 if (!res) {
4508 res = wait_file(chan, ints, "digits/hundred", lang);
4509 if (!res && year % 100 != 0) {
4510 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4511 }
4512 }
4513 }
4514 }
4515 }
4516 return res;
4517}
4518
4519/*! \brief German syntax */
4520int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4521{
4522 struct timeval when = { t, 0 };
4523 struct ast_tm tm;
4524 char fn[256];
4525 int res = 0;
4526 ast_localtime(&when, &tm, NULL);
4527 if (!res) {
4528 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4529 res = ast_streamfile(chan, fn, lang);
4530 if (!res)
4531 res = ast_waitstream(chan, ints);
4532 }
4533 if (!res)
4534 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4535 if (!res)
4536 res = ast_waitstream(chan, ints);
4537 if (!res) {
4538 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4539 res = ast_streamfile(chan, fn, lang);
4540 if (!res)
4541 res = ast_waitstream(chan, ints);
4542 }
4543 if (!res) {
4544 /* Year */
4545 int year = tm.tm_year + 1900;
4546 if (year > 1999) { /* year 2000 and later */
4547 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4548 } else {
4549 if (year < 1100) {
4550 /* I'm not going to handle 1100 and prior */
4551 /* We'll just be silent on the year, instead of bombing out. */
4552 } else {
4553 /* year 1100 to 1999. will anybody need this?!? */
4554 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4555 snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
4556 res = wait_file(chan, ints, fn, lang);
4557 if (!res) {
4558 res = wait_file(chan, ints, "digits/hundred", lang);
4559 if (!res && year % 100 != 0) {
4560 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4561 }
4562 }
4563 }
4564 }
4565 }
4566 return res;
4567}
4568
4569/*! \brief Hungarian syntax */
4570int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4571{
4572 struct timeval when = { t, 0 };
4573 struct ast_tm tm;
4574 char fn[256];
4575 int res = 0;
4576 ast_localtime(&when, &tm, NULL);
4577
4578 if (!res)
4579 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4580 if (!res)
4581 res = ast_waitstream(chan, ints);
4582 if (!res) {
4583 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4584 res = ast_streamfile(chan, fn, lang);
4585 if (!res)
4586 res = ast_waitstream(chan, ints);
4587 }
4588 if (!res)
4589 ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
4590 if (!res)
4591 res = ast_waitstream(chan, ints);
4592 if (!res) {
4593 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4594 res = ast_streamfile(chan, fn, lang);
4595 if (!res)
4596 res = ast_waitstream(chan, ints);
4597 }
4598 return res;
4599}
4600
4601/*! \brief French syntax */
4602int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4603{
4604 struct timeval when = { t, 0 };
4605 struct ast_tm tm;
4606 char fn[256];
4607 int res = 0;
4608 ast_localtime(&when, &tm, NULL);
4609 if (!res) {
4610 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4611 res = ast_streamfile(chan, fn, lang);
4612 if (!res)
4613 res = ast_waitstream(chan, ints);
4614 }
4615 if (!res)
4616 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4617 if (!res)
4618 res = ast_waitstream(chan, ints);
4619 if (!res) {
4620 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4621 res = ast_streamfile(chan, fn, lang);
4622 if (!res)
4623 res = ast_waitstream(chan, ints);
4624 }
4625 if (!res)
4626 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4627 return res;
4628}
4629
4630/*! \brief Dutch syntax */
4631int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4632{
4633 struct timeval when = { t, 0 };
4634 struct ast_tm tm;
4635 char fn[256];
4636 int res = 0;
4637 ast_localtime(&when, &tm, NULL);
4638 if (!res) {
4639 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4640 res = ast_streamfile(chan, fn, lang);
4641 if (!res)
4642 res = ast_waitstream(chan, ints);
4643 }
4644 if (!res)
4645 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4646 if (!res) {
4647 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4648 res = ast_streamfile(chan, fn, lang);
4649 if (!res)
4650 res = ast_waitstream(chan, ints);
4651 }
4652 if (!res)
4653 res = ast_waitstream(chan, ints);
4654 if (!res)
4655 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4656 return res;
4657}
4658
4659/*! \brief Thai syntax */
4660int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4661{
4662 struct timeval when = { t, 0 };
4663 struct ast_tm tm;
4664 char fn[256];
4665 int res = 0;
4666 ast_localtime(&when, &tm, NULL);
4667 if (!res) {
4668 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4669 res = ast_streamfile(chan, fn, lang);
4670 ast_copy_string(fn, "digits/tee", sizeof(fn));
4671 res = ast_streamfile(chan, fn, lang);
4672 if (!res)
4673 res = ast_waitstream(chan, ints);
4674 }
4675 if (!res)
4676 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4677 if (!res)
4678 res = ast_waitstream(chan, ints);
4679 if (!res) {
4680 ast_copy_string(fn, "digits/duan", sizeof(fn));
4681 res = ast_streamfile(chan, fn, lang);
4682 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4683 res = ast_streamfile(chan, fn, lang);
4684 if (!res)
4685 res = ast_waitstream(chan, ints);
4686 }
4687 if (!res){
4688 ast_copy_string(fn, "digits/posor", sizeof(fn));
4689 res = ast_streamfile(chan, fn, lang);
4690 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4691 }
4692 return res;
4693}
4694
4695/*! \brief Portuguese syntax */
4696int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4697{
4698 struct timeval when = { t, 0 };
4699 struct ast_tm tm;
4700 char fn[256];
4701 int res = 0;
4702
4703 ast_localtime(&when, &tm, NULL);
4704 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4705 if (!res)
4706 res = wait_file(chan, ints, fn, lang);
4707 if (!res)
4708 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4709 if (!res)
4710 res = wait_file(chan, ints, "digits/pt-de", lang);
4711 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4712 if (!res)
4713 res = wait_file(chan, ints, fn, lang);
4714 if (!res)
4715 res = wait_file(chan, ints, "digits/pt-de", lang);
4716 if (!res)
4717 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4718
4719 return res;
4720}
4721
4722/*! \brief Hebrew syntax */
4723int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4724{
4725 struct timeval when = { t, 0 };
4726 struct ast_tm tm;
4727 char fn[256];
4728 int res = 0;
4729 ast_localtime(&when, &tm, NULL);
4730 if (!res) {
4731 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4732 res = ast_streamfile(chan, fn, lang);
4733 if (!res) {
4734 res = ast_waitstream(chan, ints);
4735 }
4736 }
4737 if (!res) {
4738 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4739 res = ast_streamfile(chan, fn, lang);
4740 if (!res) {
4741 res = ast_waitstream(chan, ints);
4742 }
4743 }
4744 if (!res) {
4745 res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
4746 }
4747 if (!res) {
4748 res = ast_waitstream(chan, ints);
4749 }
4750 if (!res) {
4751 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
4752 }
4753 return res;
4754}
4755
4756/* Icelandic syntax */
4757int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4758{
4759 struct timeval when = { t, 0 };
4760 struct ast_tm tm;
4761 char fn[256];
4762 int res = 0;
4763 ast_localtime(&when, &tm, NULL);
4764 if (!res) {
4765 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4766 res = ast_streamfile(chan, fn, lang);
4767 if (!res)
4768 res = ast_waitstream(chan, ints);
4769 }
4770 if (!res)
4771 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4772 if (!res)
4773 res = ast_waitstream(chan, ints);
4774 if (!res) {
4775 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4776 res = ast_streamfile(chan, fn, lang);
4777 if (!res)
4778 res = ast_waitstream(chan, ints);
4779 }
4780 if (!res) {
4781 /* Year */
4782 int year = tm.tm_year + 1900;
4783 if (year > 1999) { /* year 2000 and later */
4784 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4785 } else {
4786 if (year < 1100) {
4787 /* I'm not going to handle 1100 and prior */
4788 /* We'll just be silent on the year, instead of bombing out. */
4789 } else {
4790 /* year 1100 to 1999. will anybody need this?!? */
4791 snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4792 res = wait_file(chan, ints, fn, lang);
4793 if (!res) {
4794 res = wait_file(chan, ints, "digits/hundred", lang);
4795 if (!res && year % 100 != 0) {
4796 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4797 }
4798 }
4799 }
4800 }
4801 }
4802 return res;
4803}
4804
4805static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4806{
4807 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4808 return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4809 } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4810 return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
4811 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4812 return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
4813 } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
4814 return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
4815 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4816 return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
4817 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4818 return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
4819 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4820 return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
4821 } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4822 return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone);
4823 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4824 return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone);
4825 } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
4826 return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
4827 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4828 return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
4829 } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
4830 return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
4831 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4832 return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
4833 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4834 return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
4835 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
4836 return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
4837 } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
4838 return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
4839 }
4840
4841 /* Default to English */
4842 return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4843}
4844
4845/*! \brief English syntax */
4846int 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)
4847{
4848 struct timeval when = { t, 0 };
4849 struct ast_tm tm;
4850 int res=0, offset, sndoffset;
4851 char sndfile[256], nextmsg[256];
4852
4853 if (format == NULL)
4854 format = "ABdY 'digits/at' IMp";
4855
4856 ast_localtime(&when, &tm, tzone);
4857
4858 for (offset=0 ; format[offset] != '\0' ; offset++) {
4859 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4860 switch (format[offset]) {
4861 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4862 case '\'':
4863 /* Literal name of a sound file */
4864 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4865 sndfile[sndoffset] = format[offset];
4866 }
4867 sndfile[sndoffset] = '\0';
4868 res = wait_file(chan, ints, sndfile, lang);
4869 break;
4870 case 'A':
4871 case 'a':
4872 /* Sunday - Saturday */
4873 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4874 res = wait_file(chan, ints, nextmsg, lang);
4875 break;
4876 case 'B':
4877 case 'b':
4878 case 'h':
4879 /* January - December */
4880 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4881 res = wait_file(chan, ints, nextmsg, lang);
4882 break;
4883 case 'm':
4884 /* Month enumerated */
4885 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
4886 break;
4887 case 'd':
4888 case 'e':
4889 /* First - Thirtyfirst */
4890 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
4891 break;
4892 case 'Y':
4893 /* Year */
4894 if (tm.tm_year > 99) {
4895 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4896 } else if (tm.tm_year < 1) {
4897 /* I'm not going to handle 1900 and prior */
4898 /* We'll just be silent on the year, instead of bombing out. */
4899 } else {
4900 res = wait_file(chan, ints, "digits/19", lang);
4901 if (!res) {
4902 if (tm.tm_year <= 9) {
4903 /* 1901 - 1909 */
4904 res = wait_file(chan, ints, "digits/oh", lang);
4905 }
4906
4907 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
4908 }
4909 }
4910 break;
4911 case 'I':
4912 case 'l':
4913 /* 12-Hour */
4914 if (tm.tm_hour == 0)
4915 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4916 else if (tm.tm_hour > 12)
4917 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4918 else
4919 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4920 res = wait_file(chan, ints, nextmsg, lang);
4921 break;
4922 case 'H':
4923 case 'k':
4924 /* 24-Hour */
4925 if (format[offset] == 'H') {
4926 /* e.g. oh-eight */
4927 if (tm.tm_hour < 10) {
4928 res = wait_file(chan, ints, "digits/oh", lang);
4929 }
4930 } else {
4931 /* e.g. eight */
4932 if (tm.tm_hour == 0) {
4933 res = wait_file(chan, ints, "digits/oh", lang);
4934 }
4935 }
4936 if (!res) {
4937 if (tm.tm_hour != 0) {
4938 int remaining = tm.tm_hour;
4939 if (tm.tm_hour > 20) {
4940 res = wait_file(chan, ints, "digits/20", lang);
4941 remaining -= 20;
4942 }
4943 if (!res) {
4944 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
4945 res = wait_file(chan, ints, nextmsg, lang);
4946 }
4947 }
4948 }
4949 break;
4950 case 'M':
4951 case 'N':
4952 /* Minute */
4953 if (tm.tm_min == 0) {
4954 if (format[offset] == 'M') {
4955 res = wait_file(chan, ints, "digits/oclock", lang);
4956 } else {
4957 res = wait_file(chan, ints, "digits/hundred", lang);
4958 }
4959 } else if (tm.tm_min < 10) {
4960 res = wait_file(chan, ints, "digits/oh", lang);
4961 if (!res) {
4962 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
4963 res = wait_file(chan, ints, nextmsg, lang);
4964 }
4965 } else {
4966 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4967 }
4968 break;
4969 case 'P':
4970 case 'p':
4971 /* AM/PM */
4972 if (tm.tm_hour > 11)
4973 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4974 else
4975 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4976 res = wait_file(chan, ints, nextmsg, lang);
4977 break;
4978 case 'Q':
4979 /* Shorthand for "Today", "Yesterday", or ABdY */
4980 /* XXX As emphasized elsewhere, this should the native way in your
4981 * language to say the date, with changes in what you say, depending
4982 * upon how recent the date is. XXX */
4983 {
4984 struct timeval now = ast_tvnow();
4985 struct ast_tm tmnow;
4986 time_t beg_today;
4987
4988 gettimeofday(&now, NULL);
4989 ast_localtime(&now, &tmnow, tzone);
4990 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4991 /* In any case, it saves not having to do ast_mktime() */
4992 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4993 if (beg_today + 15768000 < t) {
4994 /* More than 6 months from now - "April nineteenth two thousand three" */
4995 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4996 } else if (beg_today + 2628000 < t) {
4997 /* Less than 6 months from now - "August seventh" */
4998 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4999 } else if (beg_today + 86400 * 6 < t) {
5000 /* Less than a month from now - "Sunday, October third" */
5001 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5002 } else if (beg_today + 172800 < t) {
5003 /* Within the next week */
5004 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5005 } else if (beg_today + 86400 < t) {
5006 /* Tomorrow */
5007 res = wait_file(chan, ints, "digits/tomorrow", lang);
5008 } else if (beg_today < t) {
5009 /* Today */
5010 res = wait_file(chan, ints, "digits/today", lang);
5011 } else if (beg_today - 86400 < t) {
5012 /* Yesterday */
5013 res = wait_file(chan, ints, "digits/yesterday", lang);
5014 } else if (beg_today - 86400 * 6 < t) {
5015 /* Within the last week */
5016 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5017 } else if (beg_today - 2628000 < t) {
5018 /* Less than a month ago - "Sunday, October third" */
5019 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5020 } else if (beg_today - 15768000 < t) {
5021 /* Less than 6 months ago - "August seventh" */
5022 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5023 } else {
5024 /* More than 6 months ago - "April nineteenth two thousand three" */
5025 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5026 }
5027 }
5028 break;
5029 case 'q':
5030 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5031 /* XXX As emphasized elsewhere, this should the native way in your
5032 * language to say the date, with changes in what you say, depending
5033 * upon how recent the date is. XXX */
5034 {
5035 struct timeval now;
5036 struct ast_tm tmnow;
5037 time_t beg_today;
5038
5039 now = ast_tvnow();
5040 ast_localtime(&now, &tmnow, tzone);
5041 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5042 /* In any case, it saves not having to do ast_mktime() */
5043 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5044 if (beg_today + 15768000 < t) {
5045 /* More than 6 months from now - "April nineteenth two thousand three" */
5046 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5047 } else if (beg_today + 2628000 < t) {
5048 /* Less than 6 months from now - "August seventh" */
5049 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5050 } else if (beg_today + 86400 * 6 < t) {
5051 /* Less than a month from now - "Sunday, October third" */
5052 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5053 } else if (beg_today + 172800 < t) {
5054 /* Within the next week */
5055 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5056 } else if (beg_today + 86400 < t) {
5057 /* Tomorrow */
5058 res = wait_file(chan, ints, "digits/tomorrow", lang);
5059 } else if (beg_today < t) {
5060 /* Today */
5061 res = wait_file(chan, ints, "digits/today", lang);
5062 } else if (beg_today - 86400 < t) {
5063 /* Yesterday */
5064 res = wait_file(chan, ints, "digits/yesterday", lang);
5065 } else if (beg_today - 86400 * 6 < t) {
5066 /* Within the last week */
5067 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5068 } else if (beg_today - 2628000 < t) {
5069 /* Less than a month ago - "Sunday, October third" */
5070 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5071 } else if (beg_today - 15768000 < t) {
5072 /* Less than 6 months ago - "August seventh" */
5073 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5074 } else {
5075 /* More than 6 months ago - "April nineteenth two thousand three" */
5076 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5077 }
5078 }
5079 break;
5080 case 'R':
5081 res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
5082 break;
5083 case 'S':
5084 /* Seconds */
5085 if (tm.tm_sec == 0) {
5086 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5087 res = wait_file(chan, ints, nextmsg, lang);
5088 } else if (tm.tm_sec < 10) {
5089 res = wait_file(chan, ints, "digits/oh", lang);
5090 if (!res) {
5091 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5092 res = wait_file(chan, ints, nextmsg, lang);
5093 }
5094 } else {
5095 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
5096 }
5097 break;
5098 case 'T':
5099 res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
5100 break;
5101 case ' ':
5102 case ' ':
5103 /* Just ignore spaces and tabs */
5104 break;
5105 default:
5106 /* Unknown character */
5107 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5108 }
5109 /* Jump out on DTMF */
5110 if (res) {
5111 break;
5112 }
5113 }
5114 return res;
5115}
5116
5117static char next_item(const char *format)
5118{
5119 const char *next = ast_skip_blanks(format);
5120 return *next;
5121}
5122
5123/*! \brief Danish syntax */
5124int 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)
5125{
5126 struct timeval when = { t, 0 };
5127 struct ast_tm tm;
5128 int res=0, offset, sndoffset;
5129 char sndfile[256], nextmsg[256];
5130
5131 if (!format)
5132 format = "A dBY HMS";
5133
5134 ast_localtime(&when, &tm, tzone);
5135
5136 for (offset=0 ; format[offset] != '\0' ; offset++) {
5137 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5138 switch (format[offset]) {
5139 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5140 case '\'':
5141 /* Literal name of a sound file */
5142 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5143 sndfile[sndoffset] = format[offset];
5144 }
5145 sndfile[sndoffset] = '\0';
5146 res = wait_file(chan, ints, sndfile, lang);
5147 break;
5148 case 'A':
5149 case 'a':
5150 /* Sunday - Saturday */
5151 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5152 res = wait_file(chan, ints, nextmsg, lang);
5153 break;
5154 case 'B':
5155 case 'b':
5156 case 'h':
5157 /* January - December */
5158 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5159 res = wait_file(chan, ints, nextmsg, lang);
5160 break;
5161 case 'm':
5162 /* Month enumerated */
5163 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
5164 break;
5165 case 'd':
5166 case 'e':
5167 /* First - Thirtyfirst */
5168 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
5169 break;
5170 case 'Y':
5171 /* Year */
5172 {
5173 int year = tm.tm_year + 1900;
5174 if (year > 1999) { /* year 2000 and later */
5175 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
5176 } else {
5177 if (year < 1100) {
5178 /* I'm not going to handle 1100 and prior */
5179 /* We'll just be silent on the year, instead of bombing out. */
5180 } else {
5181 /* year 1100 to 1999. will anybody need this?!? */
5182 /* say 1967 as 'nineteen hundred seven and sixty' */
5183 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
5184 res = wait_file(chan, ints, nextmsg, lang);
5185 if (!res) {
5186 res = wait_file(chan, ints, "digits/hundred", lang);
5187 if (!res && year % 100 != 0) {
5188 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
5189 }
5190 }
5191 }
5192 }
5193 }
5194 break;
5195 case 'I':
5196 case 'l':
5197 /* 12-Hour */
5198 res = wait_file(chan, ints, "digits/oclock", lang);
5199 if (tm.tm_hour == 0)
5200 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5201 else if (tm.tm_hour > 12)
5202 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5203 else
5204 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5205 if (!res) {
5206 res = wait_file(chan, ints, nextmsg, lang);
5207 }
5208 break;
5209 case 'H':
5210 /* 24-Hour, single digit hours preceded by "oh" (0) */
5211 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
5212 res = wait_file(chan, ints, "digits/0", lang);
5213 }
5214 /* FALLTRHU */
5215 case 'k':
5216 /* 24-Hour */
5217 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5218 break;
5219 case 'M':
5220 /* Minute */
5221 if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
5222 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5223 }
5224 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5225 if (tm.tm_min == 1) {
5226 res = wait_file(chan, ints, "minute", lang);
5227 } else {
5228 res = wait_file(chan, ints, "minutes", lang);
5229 }
5230 }
5231 break;
5232 case 'P':
5233 case 'p':
5234 /* AM/PM */
5235 if (tm.tm_hour > 11)
5236 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5237 else
5238 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5239 res = wait_file(chan, ints, nextmsg, lang);
5240 break;
5241 case 'Q':
5242 /* Shorthand for "Today", "Yesterday", or AdBY */
5243 /* XXX As emphasized elsewhere, this should the native way in your
5244 * language to say the date, with changes in what you say, depending
5245 * upon how recent the date is. XXX */
5246 {
5247 struct timeval now = ast_tvnow();
5248 struct ast_tm tmnow;
5249 time_t beg_today;
5250
5251 ast_localtime(&now, &tmnow, tzone);
5252 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5253 /* In any case, it saves not having to do ast_mktime() */
5254 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5255 if (beg_today < t) {
5256 /* Today */
5257 res = wait_file(chan, ints, "digits/today", lang);
5258 } else if (beg_today - 86400 < t) {
5259 /* Yesterday */
5260 res = wait_file(chan, ints, "digits/yesterday", lang);
5261 } else {
5262 res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
5263 }
5264 }
5265 break;
5266 case 'q':
5267 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5268 /* XXX As emphasized elsewhere, this should the native way in your
5269 * language to say the date, with changes in what you say, depending
5270 * upon how recent the date is. XXX */
5271 {
5272 struct timeval now = ast_tvnow();
5273 struct ast_tm tmnow;
5274 time_t beg_today;
5275
5276 ast_localtime(&now, &tmnow, tzone);
5277 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5278 /* In any case, it saves not having to do ast_mktime() */
5279 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5280 if (beg_today < t) {
5281 /* Today */
5282 } else if ((beg_today - 86400) < t) {
5283 /* Yesterday */
5284 res = wait_file(chan, ints, "digits/yesterday", lang);
5285 } else if (beg_today - 86400 * 6 < t) {
5286 /* Within the last week */
5287 res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
5288 } else {
5289 res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
5290 }
5291 }
5292 break;
5293 case 'R':
5294 res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
5295 break;
5296 case 'S':
5297 /* Seconds */
5298 res = wait_file(chan, ints, find_first_and(lang), lang);
5299 if (!res) {
5300 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5301 if (!res) {
5302 res = wait_file(chan, ints, "seconds", lang);
5303 }
5304 }
5305 break;
5306 case 'T':
5307 res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
5308 break;
5309 case ' ':
5310 case ' ':
5311 /* Just ignore spaces and tabs */
5312 break;
5313 default:
5314 /* Unknown character */
5315 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5316 }
5317 /* Jump out on DTMF */
5318 if (res) {
5319 break;
5320 }
5321 }
5322 return res;
5323}
5324
5325/*! \brief German syntax */
5326int 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)
5327{
5328 struct timeval when = { t, 0 };
5329 struct ast_tm tm;
5330 int res=0, offset, sndoffset;
5331 char sndfile[256], nextmsg[256];
5332
5333 if (!format)
5334 format = "A dBY HMS";
5335
5336 ast_localtime(&when, &tm, tzone);
5337
5338 for (offset=0 ; format[offset] != '\0' ; offset++) {
5339 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5340 switch (format[offset]) {
5341 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5342 case '\'':
5343 /* Literal name of a sound file */
5344 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5345 sndfile[sndoffset] = format[offset];
5346 }
5347 sndfile[sndoffset] = '\0';
5348 res = wait_file(chan, ints, sndfile, lang);
5349 break;
5350 case 'A':
5351 case 'a':
5352 /* Sunday - Saturday */
5353 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5354 res = wait_file(chan, ints, nextmsg, lang);
5355 break;
5356 case 'B':
5357 case 'b':
5358 case 'h':
5359 /* January - December */
5360 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5361 res = wait_file(chan, ints, nextmsg, lang);
5362 break;
5363 case 'm':
5364 /* Month enumerated */
5365 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
5366 break;
5367 case 'd':
5368 case 'e':
5369 /* First - Thirtyfirst */
5370 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
5371 break;
5372 case 'Y':
5373 /* Year */
5374 {
5375 int year = tm.tm_year + 1900;
5376 if (year > 1999) { /* year 2000 and later */
5377 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
5378 } else {
5379 if (year < 1100) {
5380 /* I'm not going to handle 1100 and prior */
5381 /* We'll just be silent on the year, instead of bombing out. */
5382 } else {
5383 /* year 1100 to 1999. will anybody need this?!? */
5384 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
5385 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
5386 res = wait_file(chan, ints, nextmsg, lang);
5387 if (!res) {
5388 res = wait_file(chan, ints, "digits/hundred", lang);
5389 if (!res && year % 100 != 0) {
5390 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
5391 }
5392 }
5393 }
5394 }
5395 }
5396 break;
5397 case 'I':
5398 case 'l':
5399 /* 12-Hour */
5400 if (tm.tm_hour == 0)
5401 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5402 else if (tm.tm_hour == 1)
5403 ast_copy_string(nextmsg, "digits/1N", sizeof(nextmsg));
5404 else if (tm.tm_hour > 12)
5405 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5406 else
5407 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5408 res = wait_file(chan, ints, nextmsg, lang);
5409 if (!res) {
5410 res = wait_file(chan, ints, "digits/oclock", lang);
5411 }
5412 break;
5413 case 'H':
5414 case 'k':
5415 /* 24-Hour */
5416 if (tm.tm_hour == 1) {
5417 res = wait_file(chan, ints, "digits/1N", lang);
5418 } else {
5419 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5420 }
5421 if (!res) {
5422 res = wait_file(chan, ints, "digits/oclock", lang);
5423 }
5424 break;
5425 case 'M':
5426 /* Minute */
5427 if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
5428 res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say minutes */
5429 } else if (tm.tm_min > 0) {
5430 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5431 }
5432
5433 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5434 if (tm.tm_min == 1) {
5435 res = wait_file(chan, ints, "minute", lang);
5436 } else {
5437 res = wait_file(chan, ints, "minutes", lang);
5438 }
5439 }
5440 break;
5441 case 'P':
5442 case 'p':
5443 /* AM/PM */
5444 if (tm.tm_hour > 11)
5445 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5446 else
5447 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5448 res = wait_file(chan, ints, nextmsg, lang);
5449 break;
5450 case 'Q':
5451 /* Shorthand for "Today", "Yesterday", or AdBY */
5452 /* XXX As emphasized elsewhere, this should the native way in your
5453 * language to say the date, with changes in what you say, depending
5454 * upon how recent the date is. XXX */
5455 {
5456 struct timeval now = ast_tvnow();
5457 struct ast_tm tmnow;
5458 time_t beg_today;
5459
5460 ast_localtime(&now, &tmnow, tzone);
5461 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5462 /* In any case, it saves not having to do ast_mktime() */
5463 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5464 if (beg_today < t) {
5465 /* Today */
5466 res = wait_file(chan, ints, "digits/today", lang);
5467 } else if (beg_today - 86400 < t) {
5468 /* Yesterday */
5469 res = wait_file(chan, ints, "digits/yesterday", lang);
5470 } else {
5471 res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
5472 }
5473 }
5474 break;
5475 case 'q':
5476 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5477 /* XXX As emphasized elsewhere, this should the native way in your
5478 * language to say the date, with changes in what you say, depending
5479 * upon how recent the date is. XXX */
5480 {
5481 struct timeval now = ast_tvnow();
5482 struct ast_tm tmnow;
5483 time_t beg_today;
5484
5485 ast_localtime(&now, &tmnow, tzone);
5486 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5487 /* In any case, it saves not having to do ast_mktime() */
5488 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5489 if (beg_today < t) {
5490 /* Today */
5491 } else if ((beg_today - 86400) < t) {
5492 /* Yesterday */
5493 res = wait_file(chan, ints, "digits/yesterday", lang);
5494 } else if (beg_today - 86400 * 6 < t) {
5495 /* Within the last week */
5496 res = ast_say_date_with_format_de(chan, t, ints, lang, "A", tzone);
5497 } else {
5498 res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
5499 }
5500 }
5501 break;
5502 case 'R':
5503 res = ast_say_date_with_format_de(chan, t, ints, lang, "HM", tzone);
5504 break;
5505 case 'S':
5506 /* Seconds */
5507 res = wait_file(chan, ints, find_first_and(lang), lang);
5508 if (!res) {
5509 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5510 if (!res) {
5511 res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
5512 }
5513 }
5514 break;
5515 case 'T':
5516 res = ast_say_date_with_format_de(chan, t, ints, lang, "HMS", tzone);
5517 break;
5518 case ' ':
5519 case ' ':
5520 /* Just ignore spaces and tabs */
5521 break;
5522 default:
5523 /* Unknown character */
5524 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5525 }
5526 /* Jump out on DTMF */
5527 if (res) {
5528 break;
5529 }
5530 }
5531 return res;
5532}
5533
5534/* Icelandic syntax */
5535int 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)
5536{
5537 struct timeval when = { t, 0 };
5538 struct ast_tm tm;
5539 int res=0, offset, sndoffset;
5540 char sndfile[256], nextmsg[256];
5541
5542 if (!format)
5543 format = "A dBY HMS";
5544
5545 ast_localtime(&when, &tm, tzone);
5546
5547 for (offset=0 ; format[offset] != '\0' ; offset++) {
5548 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5549 switch (format[offset]) {
5550 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5551 case '\'':
5552 /* Literal name of a sound file */
5553 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5554 sndfile[sndoffset] = format[offset];
5555 }
5556 sndfile[sndoffset] = '\0';
5557 res = wait_file(chan, ints, sndfile, lang);
5558 break;
5559 case 'A':
5560 case 'a':
5561 /* Sunday - Saturday */
5562 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5563 res = wait_file(chan, ints, nextmsg, lang);
5564 break;
5565 case 'B':
5566 case 'b':
5567 case 'h':
5568 /* January - December */
5569 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5570 res = wait_file(chan, ints, nextmsg, lang);
5571 break;
5572 case 'm':
5573 /* Month enumerated */
5574 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
5575 break;
5576 case 'd':
5577 case 'e':
5578 /* First - Thirtyfirst */
5579 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
5580 break;
5581 case 'Y':
5582 /* Year */
5583 {
5584 int year = tm.tm_year + 1900;
5585 if (year > 1999) { /* year 2000 and later */
5586 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
5587 } else {
5588 if (year < 1100) {
5589 /* I'm not going to handle 1100 and prior */
5590 /* We'll just be silent on the year, instead of bombing out. */
5591 } else {
5592 /* year 1100 to 1999. will anybody need this?!? */
5593 /* say 1967 as 'nineteen hundred seven and sixty' */
5594 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
5595 res = wait_file(chan, ints, nextmsg, lang);
5596 if (!res) {
5597 res = wait_file(chan, ints, "digits/hundred", lang);
5598 if (!res && year % 100 != 0) {
5599 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
5600 }
5601 }
5602 }
5603 }
5604 }
5605 break;
5606 case 'I':
5607 case 'l':
5608 /* 12-Hour */
5609 res = wait_file(chan, ints, "digits/oclock", lang);
5610 if (tm.tm_hour == 0)
5611 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5612 else if (tm.tm_hour > 12)
5613 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5614 else
5615 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5616 if (!res) {
5617 res = wait_file(chan, ints, nextmsg, lang);
5618 }
5619 break;
5620 case 'H':
5621 /* 24-Hour, single digit hours preceded by "oh" (0) */
5622 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
5623 res = wait_file(chan, ints, "digits/0", lang);
5624 }
5625 /* FALLTRHU */
5626 case 'k':
5627 /* 24-Hour */
5628 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5629 break;
5630 case 'M':
5631 /* Minute */
5632 if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
5633 if (tm.tm_min < 10)
5634 res = wait_file(chan, ints, "digits/0", lang);
5635 /* Gender depends on whether or not seconds follow */
5636 if (next_item(&format[offset + 1]) == 'S')
5637 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5638 else
5639 res = ast_say_number(chan, tm.tm_min, ints, lang, "n");
5640 }
5641 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5642 /* Say minute/minutes depending on whether minutes end in 1 */
5643 if ((tm.tm_min % 10 == 1) && (tm.tm_min != 11)) {
5644 res = wait_file(chan, ints, "minute", lang);
5645 } else {
5646 res = wait_file(chan, ints, "minutes", lang);
5647 }
5648 }
5649 break;
5650 case 'P':
5651 case 'p':
5652 /* AM/PM */
5653 if (tm.tm_hour > 11)
5654 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5655 else
5656 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5657 res = wait_file(chan, ints, nextmsg, lang);
5658 break;
5659 case 'Q':
5660 /* Shorthand for "Today", "Yesterday", or AdBY */
5661 /* XXX As emphasized elsewhere, this should the native way in your
5662 * language to say the date, with changes in what you say, depending
5663 * upon how recent the date is. XXX */
5664 {
5665 struct timeval now = ast_tvnow();
5666 struct ast_tm tmnow;
5667 time_t beg_today;
5668
5669 ast_localtime(&now, &tmnow, tzone);
5670 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5671 /* In any case, it saves not having to do ast_mktime() */
5672 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5673 if (beg_today < t) {
5674 /* Today */
5675 res = wait_file(chan, ints, "digits/today", lang);
5676 } else if (beg_today - 86400 < t) {
5677 /* Yesterday */
5678 res = wait_file(chan, ints, "digits/yesterday", lang);
5679 } else {
5680 res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5681 }
5682 }
5683 break;
5684 case 'q':
5685 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5686 /* XXX As emphasized elsewhere, this should the native way in your
5687 * language to say the date, with changes in what you say, depending
5688 * upon how recent the date is. XXX */
5689 {
5690 struct timeval now = ast_tvnow();
5691 struct ast_tm tmnow;
5692 time_t beg_today;
5693
5694 ast_localtime(&now, &tmnow, tzone);
5695 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5696 /* In any case, it saves not having to do ast_mktime() */
5697 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5698 if (beg_today < t) {
5699 /* Today */
5700 } else if ((beg_today - 86400) < t) {
5701 /* Yesterday */
5702 res = wait_file(chan, ints, "digits/yesterday", lang);
5703 } else if (beg_today - 86400 * 6 < t) {
5704 /* Within the last week */
5705 res = ast_say_date_with_format_is(chan, t, ints, lang, "A", tzone);
5706 } else {
5707 res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5708 }
5709 }
5710 break;
5711 case 'R':
5712 res = ast_say_date_with_format_is(chan, t, ints, lang, "HM", tzone);
5713 break;
5714 case 'S':
5715 /* Seconds */
5716 res = wait_file(chan, ints, find_first_and(lang), lang);
5717 if (!res) {
5718 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5719 /* Say minute/minutes depending on whether seconds end in 1 */
5720 if (!res && (tm.tm_sec % 10 == 1) && (tm.tm_sec != 11)) {
5721 res = wait_file(chan, ints, "second", lang);
5722 } else {
5723 res = wait_file(chan, ints, "seconds", lang);
5724 }
5725 }
5726 break;
5727 case 'T':
5728 res = ast_say_date_with_format_is(chan, t, ints, lang, "HMS", tzone);
5729 break;
5730 case ' ':
5731 case ' ':
5732 /* Just ignore spaces and tabs */
5733 break;
5734 default:
5735 /* Unknown character */
5736 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5737 }
5738 /* Jump out on DTMF */
5739 if (res) {
5740 break;
5741 }
5742 }
5743 return res;
5744}
5745
5746/*! \brief Thai syntax */
5747int 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)
5748{
5749 struct timeval when = { t, 0 };
5750 struct ast_tm tm;
5751 int res=0, offset, sndoffset;
5752 char sndfile[256], nextmsg[256];
5753
5754 if (format == NULL)
5755 format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
5756
5757 ast_localtime(&when, &tm, tzone);
5758
5759 for (offset=0 ; format[offset] != '\0' ; offset++) {
5760 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5761 switch (format[offset]) {
5762 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5763 case '\'':
5764 /* Literal name of a sound file */
5765 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5766 sndfile[sndoffset] = format[offset];
5767 }
5768 sndfile[sndoffset] = '\0';
5769 res = wait_file(chan, ints, sndfile, lang);
5770 break;
5771 case 'A':
5772 case 'a':
5773 /* Sunday - Saturday */
5774 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5775 res = wait_file(chan, ints, nextmsg, lang);
5776 break;
5777 case 'B':
5778 case 'b':
5779 case 'h':
5780 /* January - December */
5781 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5782 res = wait_file(chan, ints, nextmsg, lang);
5783 break;
5784 case 'm':
5785 /* Month enumerated */
5786 res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
5787 break;
5788 case 'd':
5789 case 'e':
5790 /* First - Thirtyfirst */
5791 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5792 break;
5793 case 'Y':
5794 /* Year */
5795 res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
5796 break;
5797 case 'I':
5798 case 'l':
5799 /* 12-Hour */
5800 if (tm.tm_hour == 0)
5801 ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5802 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5803 res = wait_file(chan, ints, nextmsg, lang);
5804 break;
5805 case 'H':
5806 case 'k':
5807 /* 24-Hour */
5808 if (tm.tm_hour == 0)
5809 ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5810 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5811 res = wait_file(chan, ints, nextmsg, lang);
5812 break;
5813 case 'M':
5814 case 'N':
5815 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5816 break;
5817 case 'P':
5818 case 'p':
5819 break;
5820 case 'Q':
5821 /* Shorthand for "Today", "Yesterday", or ABdY */
5822 /* XXX As emphasized elsewhere, this should the native way in your
5823 * language to say the date, with changes in what you say, depending
5824 * upon how recent the date is. XXX */
5825 {
5826 struct timeval now = ast_tvnow();
5827 struct ast_tm tmnow;
5828 time_t beg_today;
5829
5830 ast_localtime(&now, &tmnow, tzone);
5831 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5832 /* In any case, it saves not having to do ast_mktime() */
5833 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5834 if (beg_today < t) {
5835 /* Today */
5836 res = wait_file(chan, ints, "digits/today", lang);
5837 } else if (beg_today - 86400 < t) {
5838 /* Yesterday */
5839 res = wait_file(chan, ints, "digits/yesterday", lang);
5840 } else if (beg_today - 86400 * 6 < t) {
5841 /* Within the last week */
5842 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5843 } else if (beg_today - 2628000 < t) {
5844 /* Less than a month ago - "Sunday, October third" */
5845 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5846 } else if (beg_today - 15768000 < t) {
5847 /* Less than 6 months ago - "August seventh" */
5848 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5849 } else {
5850 /* More than 6 months ago - "April nineteenth two thousand three" */
5851 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5852 }
5853 }
5854 break;
5855 case 'q':
5856 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5857 /* XXX As emphasized elsewhere, this should the native way in your
5858 * language to say the date, with changes in what you say, depending
5859 * upon how recent the date is. XXX */
5860 {
5861 struct timeval now = ast_tvnow();
5862 struct ast_tm tmnow;
5863 time_t beg_today;
5864
5865 ast_localtime(&now, &tmnow, tzone);
5866 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5867 /* In any case, it saves not having to do ast_mktime() */
5868 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5869 if (beg_today < t) {
5870 /* Today */
5871 } else if ((beg_today - 86400) < t) {
5872 /* Yesterday */
5873 res = wait_file(chan, ints, "digits/yesterday", lang);
5874 } else if (beg_today - 86400 * 6 < t) {
5875 /* Within the last week */
5876 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5877 } else if (beg_today - 2628000 < t) {
5878 /* Less than a month ago - "Sunday, October third" */
5879 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5880 } else if (beg_today - 15768000 < t) {
5881 /* Less than 6 months ago - "August seventh" */
5882 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5883 } else {
5884 /* More than 6 months ago - "April nineteenth two thousand three" */
5885 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5886 }
5887 }
5888 break;
5889 case 'R':
5890 res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
5891 break;
5892 case 'S':
5893 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
5894 break;
5895 case 'T':
5896 res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
5897 break;
5898 case ' ':
5899 case ' ':
5900 /* Just ignore spaces and tabs */
5901 break;
5902 default:
5903 /* Unknown character */
5904 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5905 }
5906 /* Jump out on DTMF */
5907 if (res) {
5908 break;
5909 }
5910 }
5911 return res;
5912}
5913
5914/*! \brief ast_say_date_with_format_he Say formatted date in Hebrew
5915 *
5916 * \ref ast_say_date_with_format_en for the details of the options
5917 *
5918 * Changes from the English version:
5919 *
5920 * - don't replicate in here the logic of ast_say_number_full_he
5921 *
5922 * - year is always 4-digit (because it's simpler)
5923 *
5924 * - added c, x, and X. Mainly for my tests
5925 *
5926 * - The standard "long" format used in Hebrew is AdBY, rather than ABdY
5927 *
5928 * \todo
5929 * - A "ha" is missing in the standard date format, before the 'd'.
5930 * - The numbers of 3000--19000 are not handled well
5931 */
5932int 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)
5933{
5934#define IL_DATE_STR "AdBY"
5935#define IL_TIME_STR "HM" /* NOTE: In Hebrew we do not support 12 hours, only 24. No AM or PM exists in the Hebrew language */
5936#define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
5937 /* TODO: This whole function is cut&paste from
5938 * ast_say_date_with_format_en . Is that considered acceptable?
5939 **/
5940 struct timeval when = { t, 0 };
5941 struct ast_tm tm;
5942 int res = 0, offset, sndoffset;
5943 char sndfile[256], nextmsg[256];
5944
5945 if (!format) {
5946 format = IL_DATE_STR_FULL;
5947 }
5948
5949 ast_localtime(&when, &tm, tzone);
5950
5951 for (offset = 0; format[offset] != '\0'; offset++) {
5952 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5953 switch (format[offset]) {
5954 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5955 case '\'':
5956 /* Literal name of a sound file */
5957 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5958 sndfile[sndoffset] = format[offset];
5959 }
5960 sndfile[sndoffset] = '\0';
5961 res = wait_file(chan, ints, sndfile, lang);
5962 break;
5963 case 'A':
5964 case 'a':
5965 /* Sunday - Saturday */
5966 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5967 res = wait_file(chan, ints, nextmsg, lang);
5968 break;
5969 case 'B':
5970 case 'b':
5971 case 'h':
5972 /* January - December */
5973 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5974 res = wait_file(chan, ints, nextmsg, lang);
5975 break;
5976 case 'd':
5977 case 'e': /* Day of the month */
5978 /* I'm not sure exactly what the parameters
5979 * audiofd and ctrlfd to
5980 * ast_say_number_full_he mean, but it seems
5981 * safe to pass -1 there.
5982 *
5983 * At least in one of the paths :-(
5984 */
5985 res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
5986 break;
5987 case 'Y': /* Year */
5988 res = ast_say_number_full_he(chan, tm.tm_year + 1900, ints, lang, "f", -1, -1);
5989 break;
5990 case 'I':
5991 case 'l': /* 12-Hour -> we do not support 12 hour based languages in Hebrew */
5992 case 'H':
5993 case 'k': /* 24-Hour */
5994 res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
5995 break;
5996 case 'M': /* Minute */
5997 if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
5998 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5999 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
6000 break;
6001 case 'P':
6002 case 'p':
6003 /* AM/PM - There is no AM/PM in Hebrew... */
6004 break;
6005 case 'Q':
6006 /* Shorthand for "Today", "Yesterday", or "date" */
6007 case 'q':
6008 /* Shorthand for "" (today), "Yesterday", A
6009 * (weekday), or "date" */
6010 /* XXX As emphasized elsewhere, this should the native way in your
6011 * language to say the date, with changes in what you say, depending
6012 * upon how recent the date is. XXX */
6013 {
6014 struct timeval now = ast_tvnow();
6015 struct ast_tm tmnow;
6016 time_t beg_today;
6017 char todo = format[offset]; /* The letter to format*/
6018
6019 ast_localtime(&now, &tmnow, tzone);
6020 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6021 /* In any case, it saves not having to do ast_mktime() */
6022 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6023 if (beg_today < t) {
6024 /* Today */
6025 if (todo == 'Q') {
6026 res = wait_file(chan, ints, "digits/today", lang);
6027 }
6028 } else if (beg_today - 86400 < t) {
6029 /* Yesterday */
6030 res = wait_file(chan, ints, "digits/yesterday", lang);
6031 } else if ((todo != 'Q') && (beg_today - 86400 * 6 < t)) {
6032 /* Within the last week */
6033 res = ast_say_date_with_format_he(chan, t, ints, lang, "A", tzone);
6034 } else {
6035 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
6036 }
6037 }
6038 break;
6039 case 'R':
6040 res = ast_say_date_with_format_he(chan, t, ints, lang, "HM", tzone);
6041 break;
6042 case 'S': /* Seconds */
6043 res = ast_say_number_full_he(chan, tm.tm_sec,
6044 ints, lang, "f", -1, -1
6045 );
6046 break;
6047 case 'T':
6048 res = ast_say_date_with_format_he(chan, t, ints, lang, "HMS", tzone);
6049 break;
6050 /* c, x, and X seem useful for testing. Not sure
6051 * if they're good for the general public */
6052 case 'c':
6053 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR_FULL, tzone);
6054 break;
6055 case 'x':
6056 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
6057 break;
6058 case 'X': /* Currently not locale-dependent...*/
6059 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_TIME_STR, tzone);
6060 break;
6061 case ' ':
6062 case ' ':
6063 /* Just ignore spaces and tabs */
6064 break;
6065 default:
6066 /* Unknown character */
6067 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6068 }
6069 /* Jump out on DTMF */
6070 if (res) {
6071 break;
6072 }
6073 }
6074 return res;
6075}
6076
6077
6078/*! \brief Spanish syntax */
6079int 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)
6080{
6081 struct timeval when = { t, 0 };
6082 struct ast_tm tm;
6083 int res=0, offset, sndoffset;
6084 char sndfile[256], nextmsg[256];
6085
6086 if (format == NULL)
6087 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
6088
6089 ast_localtime(&when, &tm, tzone);
6090
6091 for (offset=0 ; format[offset] != '\0' ; offset++) {
6092 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6093 switch (format[offset]) {
6094 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6095 case '\'':
6096 /* Literal name of a sound file */
6097 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6098 sndfile[sndoffset] = format[offset];
6099 }
6100 sndfile[sndoffset] = '\0';
6101 snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
6102 res = wait_file(chan, ints, nextmsg, lang);
6103 break;
6104 case 'A':
6105 case 'a':
6106 /* Sunday - Saturday */
6107 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6108 res = wait_file(chan, ints, nextmsg, lang);
6109 break;
6110 case 'B':
6111 case 'b':
6112 case 'h':
6113 /* January - December */
6114 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6115 res = wait_file(chan, ints, nextmsg, lang);
6116 break;
6117 case 'm':
6118 /* First - Twelfth */
6119 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6120 res = wait_file(chan, ints, nextmsg, lang);
6121 break;
6122 case 'd':
6123 case 'e':
6124 /* First - Thirtyfirst */
6125 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6126 break;
6127 case 'Y':
6128 /* Year */
6129 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6130 break;
6131 case 'I':
6132 case 'l':
6133 /* 12-Hour */
6134 if (tm.tm_hour == 0)
6135 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6136 else if (tm.tm_hour == 1 || tm.tm_hour == 13)
6137 snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
6138 else if (tm.tm_hour > 12)
6139 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6140 else
6141 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6142 res = wait_file(chan, ints, nextmsg, lang);
6143 break;
6144 case 'H':
6145 case 'k':
6146 /* 24-Hour */
6147 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
6148 break;
6149 case 'M':
6150 /* Minute */
6151 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6152 break;
6153 case 'P':
6154 case 'p':
6155 /* AM/PM */
6156 if (tm.tm_hour > 18)
6157 res = wait_file(chan, ints, "digits/p-m", lang);
6158 else if (tm.tm_hour > 12)
6159 res = wait_file(chan, ints, "digits/afternoon", lang);
6160 else if (tm.tm_hour)
6161 res = wait_file(chan, ints, "digits/a-m", lang);
6162 break;
6163 case 'Q':
6164 /* Shorthand for "Today", "Yesterday", or ABdY */
6165 /* XXX As emphasized elsewhere, this should the native way in your
6166 * language to say the date, with changes in what you say, depending
6167 * upon how recent the date is. XXX */
6168 {
6169 struct timeval now = ast_tvnow();
6170 struct ast_tm tmnow;
6171 time_t beg_today;
6172
6173 ast_localtime(&now, &tmnow, tzone);
6174 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6175 /* In any case, it saves not having to do ast_mktime() */
6176 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6177 if (beg_today < t) {
6178 /* Today */
6179 res = wait_file(chan, ints, "digits/today", lang);
6180 } else if (beg_today - 86400 < t) {
6181 /* Yesterday */
6182 res = wait_file(chan, ints, "digits/yesterday", lang);
6183 } else {
6184 res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
6185 }
6186 }
6187 break;
6188 case 'q':
6189 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6190 /* XXX As emphasized elsewhere, this should the native way in your
6191 * language to say the date, with changes in what you say, depending
6192 * upon how recent the date is. XXX */
6193 {
6194 struct timeval now = ast_tvnow();
6195 struct ast_tm tmnow;
6196 time_t beg_today;
6197
6198 ast_localtime(&now, &tmnow, tzone);
6199 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6200 /* In any case, it saves not having to do ast_mktime() */
6201 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6202 if (beg_today < t) {
6203 /* Today */
6204 res = wait_file(chan, ints, "digits/today", lang);
6205 } else if ((beg_today - 86400) < t) {
6206 /* Yesterday */
6207 res = wait_file(chan, ints, "digits/yesterday", lang);
6208 } else if (beg_today - 86400 * 6 < t) {
6209 /* Within the last week */
6210 res = ast_say_date_with_format_es(chan, t, ints, lang, "A", tzone);
6211 } else {
6212 res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
6213 }
6214 }
6215 break;
6216 case 'R':
6217 res = ast_say_date_with_format_es(chan, t, ints, lang, "H 'digits/y' M", tzone);
6218 break;
6219 case 'S':
6220 /* Seconds */
6221 if (tm.tm_sec == 0) {
6222 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6223 res = wait_file(chan, ints, nextmsg, lang);
6224 } else if (tm.tm_sec < 10) {
6225 res = wait_file(chan, ints, "digits/oh", lang);
6226 if (!res) {
6227 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6228 res = wait_file(chan, ints, nextmsg, lang);
6229 }
6230 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6231 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6232 res = wait_file(chan, ints, nextmsg, lang);
6233 } else {
6234 int ten, one;
6235 ten = (tm.tm_sec / 10) * 10;
6236 one = (tm.tm_sec % 10);
6237 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6238 res = wait_file(chan, ints, nextmsg, lang);
6239 if (!res) {
6240 /* Fifty, not fifty-zero */
6241 if (one != 0) {
6242 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6243 res = wait_file(chan, ints, nextmsg, lang);
6244 }
6245 }
6246 }
6247 break;
6248 case 'T':
6249 res = ast_say_date_with_format_es(chan, t, ints, lang, "HMS", tzone);
6250 break;
6251 case ' ':
6252 case ' ':
6253 /* Just ignore spaces and tabs */
6254 break;
6255 default:
6256 /* Unknown character */
6257 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6258 }
6259 /* Jump out on DTMF */
6260 if (res) {
6261 break;
6262 }
6263 }
6264 return res;
6265}
6266
6267/*! \brief French syntax
6268oclock = heure
6269*/
6270int 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)
6271{
6272 struct timeval when = { t, 0 };
6273 struct ast_tm tm;
6274 int res=0, offset, sndoffset;
6275 char sndfile[256], nextmsg[256];
6276
6277 if (format == NULL)
6278 format = "AdBY 'digits/at' IMp";
6279
6280 ast_localtime(&when, &tm, tzone);
6281
6282 for (offset=0 ; format[offset] != '\0' ; offset++) {
6283 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6284 switch (format[offset]) {
6285 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6286 case '\'':
6287 /* Literal name of a sound file */
6288 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6289 sndfile[sndoffset] = format[offset];
6290 }
6291 sndfile[sndoffset] = '\0';
6292 res = wait_file(chan, ints, sndfile, lang);
6293 break;
6294 case 'A':
6295 case 'a':
6296 /* Sunday - Saturday */
6297 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6298 res = wait_file(chan, ints, nextmsg, lang);
6299 break;
6300 case 'B':
6301 case 'b':
6302 case 'h':
6303 /* January - December */
6304 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6305 res = wait_file(chan, ints, nextmsg, lang);
6306 break;
6307 case 'm':
6308 /* First - Twelfth */
6309 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6310 res = wait_file(chan, ints, nextmsg, lang);
6311 break;
6312 case 'd':
6313 case 'e':
6314 /* First */
6315 if (tm.tm_mday == 1) {
6316 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
6317 res = wait_file(chan, ints, nextmsg, lang);
6318 } else {
6319 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
6320 }
6321 break;
6322 case 'Y':
6323 /* Year */
6324 if (tm.tm_year > 99) {
6325 res = wait_file(chan, ints, "digits/2", lang);
6326 if (!res) {
6327 res = wait_file(chan, ints, "digits/thousand", lang);
6328 }
6329 if (tm.tm_year > 100) {
6330 if (!res) {
6331 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
6332 }
6333 }
6334 } else {
6335 if (tm.tm_year < 1) {
6336 /* I'm not going to handle 1900 and prior */
6337 /* We'll just be silent on the year, instead of bombing out. */
6338 } else {
6339 res = wait_file(chan, ints, "digits/thousand", lang);
6340 if (!res) {
6341 wait_file(chan, ints, "digits/9", lang);
6342 wait_file(chan, ints, "digits/hundred", lang);
6343 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
6344 }
6345 }
6346 }
6347 break;
6348 case 'I':
6349 case 'l':
6350 /* 12-Hour */
6351 if (tm.tm_hour == 0)
6352 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6353 else if (tm.tm_hour > 12)
6354 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6355 else
6356 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6357 res = wait_file(chan, ints, nextmsg, lang);
6358 if (!res)
6359 res = wait_file(chan, ints, "digits/oclock", lang);
6360 break;
6361 case 'H':
6362 case 'k':
6363 /* 24-Hour */
6364 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
6365 if (!res)
6366 res = wait_file(chan, ints, "digits/oclock", lang);
6367 break;
6368 case 'M':
6369 /* Minute */
6370 if (tm.tm_min == 0) {
6371 break;
6372 }
6373 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
6374 break;
6375 case 'P':
6376 case 'p':
6377 /* AM/PM */
6378 if (tm.tm_hour > 11)
6379 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6380 else
6381 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6382 res = wait_file(chan, ints, nextmsg, lang);
6383 break;
6384 case 'Q':
6385 /* Shorthand for "Today", "Yesterday", or AdBY */
6386 /* XXX As emphasized elsewhere, this should the native way in your
6387 * language to say the date, with changes in what you say, depending
6388 * upon how recent the date is. XXX */
6389 {
6390 struct timeval now = ast_tvnow();
6391 struct ast_tm tmnow;
6392 time_t beg_today;
6393
6394 ast_localtime(&now, &tmnow, tzone);
6395 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6396 /* In any case, it saves not having to do ast_mktime() */
6397 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6398 if (beg_today < t) {
6399 /* Today */
6400 res = wait_file(chan, ints, "digits/today", lang);
6401 } else if (beg_today - 86400 < t) {
6402 /* Yesterday */
6403 res = wait_file(chan, ints, "digits/yesterday", lang);
6404 } else {
6405 res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
6406 }
6407 }
6408 break;
6409 case 'q':
6410 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6411 /* XXX As emphasized elsewhere, this should the native way in your
6412 * language to say the date, with changes in what you say, depending
6413 * upon how recent the date is. XXX */
6414 {
6415 struct timeval now = ast_tvnow();
6416 struct ast_tm tmnow;
6417 time_t beg_today;
6418
6419 ast_localtime(&now, &tmnow, tzone);
6420 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6421 /* In any case, it saves not having to do ast_mktime() */
6422 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6423 if (beg_today < t) {
6424 /* Today */
6425 } else if ((beg_today - 86400) < t) {
6426 /* Yesterday */
6427 res = wait_file(chan, ints, "digits/yesterday", lang);
6428 } else if (beg_today - 86400 * 6 < t) {
6429 /* Within the last week */
6430 res = ast_say_date_with_format_fr(chan, t, ints, lang, "A", tzone);
6431 } else {
6432 res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
6433 }
6434 }
6435 break;
6436 case 'R':
6437 res = ast_say_date_with_format_fr(chan, t, ints, lang, "HM", tzone);
6438 break;
6439 case 'S':
6440 /* Seconds */
6441 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
6442 if (!res) {
6443 res = wait_file(chan, ints, "second", lang);
6444 }
6445 break;
6446 case 'T':
6447 res = ast_say_date_with_format_fr(chan, t, ints, lang, "HMS", tzone);
6448 break;
6449 case ' ':
6450 case ' ':
6451 /* Just ignore spaces and tabs */
6452 break;
6453 default:
6454 /* Unknown character */
6455 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6456 }
6457 /* Jump out on DTMF */
6458 if (res) {
6459 break;
6460 }
6461 }
6462 return res;
6463}
6464
6465/*! \brief Italian syntax */
6466int 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)
6467{
6468 struct timeval when = { t, 0 };
6469 struct ast_tm tm;
6470 int res=0, offset, sndoffset;
6471 char sndfile[256], nextmsg[256];
6472
6473 if (format == NULL)
6474 format = "AdB 'digits/at' IMp";
6475
6476 ast_localtime(&when, &tm, tzone);
6477
6478 for (offset=0 ; format[offset] != '\0' ; offset++) {
6479 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6480 switch (format[offset]) {
6481 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6482 case '\'':
6483 /* Literal name of a sound file */
6484 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6485 sndfile[sndoffset] = format[offset];
6486 }
6487 sndfile[sndoffset] = '\0';
6488 res = wait_file(chan, ints, sndfile, lang);
6489 break;
6490 case 'A':
6491 case 'a':
6492 /* Sunday - Saturday */
6493 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6494 res = wait_file(chan, ints, nextmsg, lang);
6495 break;
6496 case 'B':
6497 case 'b':
6498 case 'h':
6499 /* January - December */
6500 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6501 res = wait_file(chan, ints, nextmsg, lang);
6502 break;
6503 case 'm':
6504 /* First - Twelfth */
6505 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6506 res = wait_file(chan, ints, nextmsg, lang);
6507 break;
6508 case 'd':
6509 case 'e':
6510 /* First day of the month is spelled as ordinal */
6511 if (tm.tm_mday == 1) {
6512 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
6513 res = wait_file(chan, ints, nextmsg, lang);
6514 } else {
6515 if (!res) {
6516 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6517 }
6518 }
6519 break;
6520 case 'Y':
6521 /* Year */
6522 if (tm.tm_year > 99) {
6523 res = wait_file(chan, ints, "digits/ore-2000", lang);
6524 if (tm.tm_year > 100) {
6525 if (!res) {
6526 /* This works until the end of 2021 */
6527 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
6528 res = wait_file(chan, ints, nextmsg, lang);
6529 }
6530 }
6531 } else {
6532 if (tm.tm_year < 1) {
6533 /* I'm not going to handle 1900 and prior */
6534 /* We'll just be silent on the year, instead of bombing out. */
6535 } else {
6536 res = wait_file(chan, ints, "digits/ore-1900", lang);
6537 if ((!res) && (tm.tm_year != 0)) {
6538 if (tm.tm_year <= 21) {
6539 /* 1910 - 1921 */
6540 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6541 res = wait_file(chan, ints, nextmsg, lang);
6542 } else {
6543 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
6544 int ten, one;
6545 ten = tm.tm_year / 10;
6546 one = tm.tm_year % 10;
6547 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6548 res = wait_file(chan, ints, nextmsg, lang);
6549 if (!res) {
6550 if (one != 0) {
6551 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6552 res = wait_file(chan, ints, nextmsg, lang);
6553 }
6554 }
6555 }
6556 }
6557 }
6558 }
6559 break;
6560 case 'I':
6561 case 'l':
6562 /* 12-Hour */
6563 if (tm.tm_hour == 0) {
6564 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6565 } else if (tm.tm_hour > 12) {
6566 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6567 } else {
6568 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6569 }
6570 res = wait_file(chan, ints, nextmsg, lang);
6571 break;
6572 case 'H':
6573 case 'k':
6574 /* 24-Hour */
6575 if (tm.tm_hour == 0) {
6576 res = wait_file(chan, ints, "digits/ore-mezzanotte", lang);
6577 } else if (tm.tm_hour == 1) {
6578 res = wait_file(chan, ints, "digits/ore-una", lang);
6579 } else {
6580 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6581 }
6582 break;
6583 case 'M':
6584 /* Minute */
6585 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6586 break;
6587 case 'P':
6588 case 'p':
6589 /* AM/PM */
6590 if (tm.tm_hour > 11) {
6591 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6592 } else {
6593 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6594 }
6595 res = wait_file(chan, ints, nextmsg, lang);
6596 break;
6597 case 'Q':
6598 /* Shorthand for "Today", "Yesterday", or ABdY */
6599 /* XXX As emphasized elsewhere, this should the native way in your
6600 * language to say the date, with changes in what you say, depending
6601 * upon how recent the date is. XXX */
6602 {
6603 struct timeval now = ast_tvnow();
6604 struct ast_tm tmnow;
6605 time_t beg_today;
6606
6607 ast_localtime(&now, &tmnow, tzone);
6608 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6609 /* In any case, it saves not having to do ast_mktime() */
6610 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6611 if (beg_today < t) {
6612 /* Today */
6613 res = wait_file(chan, ints, "digits/today", lang);
6614 } else if (beg_today - 86400 < t) {
6615 /* Yesterday */
6616 res = wait_file(chan, ints, "digits/yesterday", lang);
6617 } else {
6618 res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6619 }
6620 }
6621 break;
6622 case 'q':
6623 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6624 {
6625 struct timeval now = ast_tvnow();
6626 struct ast_tm tmnow;
6627 time_t beg_today;
6628
6629 ast_localtime(&now, &tmnow, tzone);
6630 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6631 /* In any case, it saves not having to do ast_mktime() */
6632 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6633 if (beg_today < t) {
6634 /* Today */
6635 } else if ((beg_today - 86400) < t) {
6636 /* Yesterday */
6637 res = wait_file(chan, ints, "digits/yesterday", lang);
6638 } else if (beg_today - 86400 * 6 < t) {
6639 /* Within the last week */
6640 res = ast_say_date_with_format_it(chan, t, ints, lang, "A", tzone);
6641 } else {
6642 res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6643 }
6644 }
6645 break;
6646 case 'R':
6647 res = ast_say_date_with_format_it(chan, t, ints, lang, "HM", tzone);
6648 break;
6649 case 'S':
6650 /* Seconds */
6651 if (tm.tm_sec == 0) {
6652 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6653 res = wait_file(chan, ints, nextmsg, lang);
6654 } else if (tm.tm_sec < 10) {
6655 res = wait_file(chan, ints, "digits/oh", lang);
6656 if (!res) {
6657 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6658 res = wait_file(chan, ints, nextmsg, lang);
6659 }
6660 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6661 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6662 res = wait_file(chan, ints, nextmsg, lang);
6663 } else {
6664 int ten, one;
6665 ten = (tm.tm_sec / 10) * 10;
6666 one = (tm.tm_sec % 10);
6667 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6668 res = wait_file(chan, ints, nextmsg, lang);
6669 if (!res) {
6670 /* Fifty, not fifty-zero */
6671 if (one != 0) {
6672 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6673 res = wait_file(chan, ints, nextmsg, lang);
6674 }
6675 }
6676 }
6677 break;
6678 case 'T':
6679 res = ast_say_date_with_format_it(chan, t, ints, lang, "HMS", tzone);
6680 break;
6681 case ' ':
6682 case ' ':
6683 /* Just ignore spaces and tabs */
6684 break;
6685 default:
6686 /* Unknown character */
6687 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6688 }
6689 /* Jump out on DTMF */
6690 if (res) {
6691 break;
6692 }
6693 }
6694 return res;
6695}
6696
6697/*! \brief Dutch syntax */
6698int 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)
6699{
6700 struct timeval when = { t, 0 };
6701 struct ast_tm tm;
6702 int res=0, offset, sndoffset;
6703 char sndfile[256], nextmsg[256];
6704
6705 if (format == NULL)
6706 format = "AdBY 'digits/at' IMp";
6707
6708 ast_localtime(&when, &tm, tzone);
6709
6710 for (offset=0 ; format[offset] != '\0' ; offset++) {
6711 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6712 switch (format[offset]) {
6713 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6714 case '\'':
6715 /* Literal name of a sound file */
6716 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6717 sndfile[sndoffset] = format[offset];
6718 }
6719 sndfile[sndoffset] = '\0';
6720 res = wait_file(chan, ints, sndfile, lang);
6721 break;
6722 case 'A':
6723 case 'a':
6724 /* Sunday - Saturday */
6725 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6726 res = wait_file(chan, ints, nextmsg, lang);
6727 break;
6728 case 'B':
6729 case 'b':
6730 case 'h':
6731 /* January - December */
6732 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6733 res = wait_file(chan, ints, nextmsg, lang);
6734 break;
6735 case 'm':
6736 /* First - Twelfth */
6737 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6738 res = wait_file(chan, ints, nextmsg, lang);
6739 break;
6740 case 'd':
6741 case 'e':
6742 /* First - Thirtyfirst */
6743 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
6744 break;
6745 case 'Y':
6746 /* Year */
6747 if (tm.tm_year > 99) {
6748 res = wait_file(chan, ints, "digits/2", lang);
6749 if (!res) {
6750 res = wait_file(chan, ints, "digits/thousand", lang);
6751 }
6752 if (tm.tm_year > 100) {
6753 if (!res) {
6754 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char *) NULL);
6755 }
6756 }
6757 } else {
6758 if (tm.tm_year < 1) {
6759 /* I'm not going to handle 1900 and prior */
6760 /* We'll just be silent on the year, instead of bombing out. */
6761 } else {
6762 res = wait_file(chan, ints, "digits/19", lang);
6763 if (!res) {
6764 if (tm.tm_year <= 9) {
6765 /* 1901 - 1909 */
6766 res = wait_file(chan, ints, "digits/oh", lang);
6767 if (!res) {
6768 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6769 res = wait_file(chan, ints, nextmsg, lang);
6770 }
6771 } else if (tm.tm_year <= 20) {
6772 /* 1910 - 1920 */
6773 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6774 res = wait_file(chan, ints, nextmsg, lang);
6775 } else {
6776 /* 1921 - 1999 */
6777 int ten, one;
6778 ten = tm.tm_year / 10;
6779 one = tm.tm_year % 10;
6780 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6781 res = wait_file(chan, ints, nextmsg, lang);
6782 if (!res) {
6783 if (one != 0) {
6784 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6785 res = wait_file(chan, ints, nextmsg, lang);
6786 }
6787 }
6788 }
6789 }
6790 }
6791 }
6792 break;
6793 case 'I':
6794 case 'l':
6795 /* 12-Hour */
6796 if (tm.tm_hour == 0)
6797 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6798 else if (tm.tm_hour > 12)
6799 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6800 else
6801 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6802 res = wait_file(chan, ints, nextmsg, lang);
6803 break;
6804 case 'H':
6805 case 'k':
6806 /* 24-Hour */
6807 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6808 if (!res) {
6809 res = wait_file(chan, ints, "digits/nl-uur", lang);
6810 }
6811 break;
6812 case 'M':
6813 /* Minute */
6814 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6815 break;
6816 case 'P':
6817 case 'p':
6818 /* AM/PM */
6819 if (tm.tm_hour > 11)
6820 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6821 else
6822 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6823 res = wait_file(chan, ints, nextmsg, lang);
6824 break;
6825 case 'Q':
6826 /* Shorthand for "Today", "Yesterday", or AdBY */
6827 /* XXX As emphasized elsewhere, this should the native way in your
6828 * language to say the date, with changes in what you say, depending
6829 * upon how recent the date is. XXX */
6830 {
6831 struct timeval now = ast_tvnow();
6832 struct ast_tm tmnow;
6833 time_t beg_today;
6834
6835 ast_localtime(&now, &tmnow, tzone);
6836 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6837 /* In any case, it saves not having to do ast_mktime() */
6838 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6839 if (beg_today < t) {
6840 /* Today */
6841 res = wait_file(chan, ints, "digits/today", lang);
6842 } else if (beg_today - 86400 < t) {
6843 /* Yesterday */
6844 res = wait_file(chan, ints, "digits/yesterday", lang);
6845 } else {
6846 res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6847 }
6848 }
6849 break;
6850 case 'q':
6851 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6852 {
6853 struct timeval now = ast_tvnow();
6854 struct ast_tm tmnow;
6855 time_t beg_today;
6856
6857 ast_localtime(&now, &tmnow, tzone);
6858 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6859 /* In any case, it saves not having to do ast_mktime() */
6860 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6861 if (beg_today < t) {
6862 /* Today */
6863 } else if ((beg_today - 86400) < t) {
6864 /* Yesterday */
6865 res = wait_file(chan, ints, "digits/yesterday", lang);
6866 } else if (beg_today - 86400 * 6 < t) {
6867 /* Within the last week */
6868 res = ast_say_date_with_format_nl(chan, t, ints, lang, "A", tzone);
6869 } else {
6870 res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6871 }
6872 }
6873 break;
6874 case 'R':
6875 res = ast_say_date_with_format_nl(chan, t, ints, lang, "HM", tzone);
6876 break;
6877 case 'S':
6878 /* Seconds */
6879 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
6880 break;
6881 case 'T':
6882 res = ast_say_date_with_format_nl(chan, t, ints, lang, "HMS", tzone);
6883 break;
6884 case ' ':
6885 case ' ':
6886 /* Just ignore spaces and tabs */
6887 break;
6888 default:
6889 /* Unknown character */
6890 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6891 }
6892 /* Jump out on DTMF */
6893 if (res) {
6894 break;
6895 }
6896 }
6897 return res;
6898}
6899
6900/*! \brief Polish syntax */
6901int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *tzone)
6902{
6903 struct timeval when = { thetime, 0 };
6904 struct ast_tm tm;
6905 int res=0, offset, sndoffset;
6906 char sndfile[256], nextmsg[256];
6907
6908 ast_localtime(&when, &tm, tzone);
6909
6910 for (offset = 0 ; format[offset] != '\0' ; offset++) {
6911 int remaining;
6912 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6913 switch (format[offset]) {
6914 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6915 case '\'':
6916 /* Literal name of a sound file */
6917 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6918 sndfile[sndoffset] = format[offset];
6919 }
6920 sndfile[sndoffset] = '\0';
6921 res = wait_file(chan, ints, sndfile, lang);
6922 break;
6923 case 'A':
6924 case 'a':
6925 /* Sunday - Saturday */
6926 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6927 res = wait_file(chan, ints, nextmsg, lang);
6928 break;
6929 case 'B':
6930 case 'b':
6931 case 'h':
6932 /* January - December */
6933 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6934 res = wait_file(chan, ints, nextmsg, lang);
6935 break;
6936 case 'm':
6937 /* Month enumerated */
6938 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
6939 break;
6940 case 'd':
6941 case 'e':
6942 /* First - Thirtyfirst */
6943 remaining = tm.tm_mday;
6944 if (tm.tm_mday > 30) {
6945 res = wait_file(chan, ints, "digits/h-30", lang);
6946 remaining -= 30;
6947 }
6948 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
6949 res = wait_file(chan, ints, "digits/h-20", lang);
6950 remaining -= 20;
6951 }
6952 if (!res) {
6953 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remaining);
6954 res = wait_file(chan, ints, nextmsg, lang);
6955 }
6956 break;
6957 case 'Y':
6958 /* Year */
6959 if (tm.tm_year > 100) {
6960 res = wait_file(chan, ints, "digits/2", lang);
6961 if (!res)
6962 res = wait_file(chan, ints, "digits/1000.2", lang);
6963 if (tm.tm_year > 100) {
6964 if (!res)
6965 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
6966 }
6967 } else if (tm.tm_year == 100) {
6968 res = wait_file(chan, ints, "digits/h-2000", lang);
6969 } else {
6970 if (tm.tm_year < 1) {
6971 /* I'm not going to handle 1900 and prior */
6972 /* We'll just be silent on the year, instead of bombing out. */
6973 break;
6974 } else {
6975 res = wait_file(chan, ints, "digits/1000", lang);
6976 if (!res) {
6977 wait_file(chan, ints, "digits/900", lang);
6978 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
6979 }
6980 }
6981 }
6982 if (!res)
6983 wait_file(chan, ints, "digits/year", lang);
6984 break;
6985 case 'I':
6986 case 'l':
6987 /* 12-Hour */
6988 if (tm.tm_hour == 0)
6989 ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
6990 else if (tm.tm_hour > 12)
6991 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
6992 else
6993 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6994
6995 res = wait_file(chan, ints, nextmsg, lang);
6996 break;
6997 case 'H':
6998 case 'k':
6999 /* 24-Hour */
7000 if (tm.tm_hour != 0) {
7001 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
7002 res = wait_file(chan, ints, nextmsg, lang);
7003 } else
7004 res = wait_file(chan, ints, "digits/t-24", lang);
7005 break;
7006 case 'M':
7007 case 'N':
7008 /* Minute */
7009 if (tm.tm_min == 0) {
7010 if (format[offset] == 'M') {
7011 res = wait_file(chan, ints, "digits/oclock", lang);
7012 } else {
7013 res = wait_file(chan, ints, "digits/100", lang);
7014 }
7015 } else
7016 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7017 break;
7018 case 'P':
7019 case 'p':
7020 /* AM/PM */
7021 if (tm.tm_hour > 11)
7022 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
7023 else
7024 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
7025 res = wait_file(chan, ints, nextmsg, lang);
7026 break;
7027 case 'Q':
7028 /* Shorthand for "Today", "Yesterday", or AdBY */
7029 {
7030 struct timeval now = ast_tvnow();
7031 struct ast_tm tmnow;
7032 time_t beg_today;
7033
7034 ast_localtime(&now, &tmnow, tzone);
7035 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7036 /* In any case, it saves not having to do ast_mktime() */
7037 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7038 if (beg_today < thetime) {
7039 /* Today */
7040 res = wait_file(chan, ints, "digits/today", lang);
7041 } else if (beg_today - 86400 < thetime) {
7042 /* Yesterday */
7043 res = wait_file(chan, ints, "digits/yesterday", lang);
7044 } else {
7045 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
7046 }
7047 }
7048 break;
7049 case 'q':
7050 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
7051 {
7052 struct timeval now = ast_tvnow();
7053 struct ast_tm tmnow;
7054 time_t beg_today;
7055
7056 ast_localtime(&now, &tmnow, tzone);
7057 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7058 /* In any case, it saves not having to do ast_mktime() */
7059 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7060 if (beg_today < thetime) {
7061 /* Today */
7062 } else if ((beg_today - 86400) < thetime) {
7063 /* Yesterday */
7064 res = wait_file(chan, ints, "digits/yesterday", lang);
7065 } else if (beg_today - 86400 * 6 < thetime) {
7066 /* Within the last week */
7067 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", tzone);
7068 } else {
7069 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
7070 }
7071 }
7072 break;
7073 case 'R':
7074 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", tzone);
7075 break;
7076 case 'S':
7077 /* Seconds */
7078 res = wait_file(chan, ints, find_first_and(lang), lang);
7079 if (!res) {
7080 if (tm.tm_sec == 1) {
7081 res = wait_file(chan, ints, "digits/1z", lang);
7082 if (!res)
7083 res = wait_file(chan, ints, "digits/second-a", lang);
7084 } else {
7085 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
7086 if (!res) {
7087 int ten, one;
7088 ten = tm.tm_sec / 10;
7089 one = tm.tm_sec % 10;
7090
7091 if (one > 1 && one < 5 && ten != 1)
7092 res = wait_file(chan, ints, "seconds", lang);
7093 else
7094 res = wait_file(chan, ints, "second", lang);
7095 }
7096 }
7097 }
7098 break;
7099 case 'T':
7100 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", tzone);
7101 break;
7102 case ' ':
7103 case ' ':
7104 /* Just ignore spaces and tabs */
7105 break;
7106 default:
7107 /* Unknown character */
7108 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
7109 }
7110 /* Jump out on DTMF */
7111 if (res)
7112 break;
7113 }
7114 return res;
7115}
7116
7117/*! \brief Portuguese syntax */
7118int 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)
7119{
7120 struct timeval when = { t, 0 };
7121 struct ast_tm tm;
7122 int res=0, offset, sndoffset;
7123 char sndfile[256], nextmsg[256];
7124
7125 if (format == NULL)
7126 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
7127
7128 ast_localtime(&when, &tm, tzone);
7129
7130 for (offset=0 ; format[offset] != '\0' ; offset++) {
7131 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
7132 switch (format[offset]) {
7133 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
7134 case '\'':
7135 /* Literal name of a sound file */
7136 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
7137 sndfile[sndoffset] = format[offset];
7138 }
7139 sndfile[sndoffset] = '\0';
7140 snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
7141 res = wait_file(chan, ints, nextmsg, lang);
7142 break;
7143 case 'A':
7144 case 'a':
7145 /* Sunday - Saturday */
7146 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
7147 res = wait_file(chan, ints, nextmsg, lang);
7148 break;
7149 case 'B':
7150 case 'b':
7151 case 'h':
7152 /* January - December */
7153 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
7154 res = wait_file(chan, ints, nextmsg, lang);
7155 break;
7156 case 'm':
7157 /* First - Twelfth */
7158 if (!strcasecmp(lang, "pt_BR")) {
7159 res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
7160 } else {
7161 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
7162 res = wait_file(chan, ints, nextmsg, lang);
7163 }
7164 break;
7165 case 'd':
7166 case 'e':
7167 /* First - Thirtyfirst */
7168 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7169 break;
7170 case 'Y':
7171 /* Year */
7172 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7173 break;
7174 case 'I':
7175 case 'l':
7176 /* 12-Hour */
7177 if (!strcasecmp(lang, "pt_BR")) {
7178 if (tm.tm_hour == 0) {
7179 if (format[offset] == 'I')
7180 res = wait_file(chan, ints, "digits/pt-a", lang);
7181 if (!res)
7182 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
7183 } else if (tm.tm_hour == 12) {
7184 if (format[offset] == 'I')
7185 res = wait_file(chan, ints, "digits/pt-ao", lang);
7186 if (!res)
7187 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
7188 } else {
7189 if (format[offset] == 'I') {
7190 if ((tm.tm_hour % 12) != 1)
7191 res = wait_file(chan, ints, "digits/pt-as", lang);
7192 else
7193 res = wait_file(chan, ints, "digits/pt-a", lang);
7194 }
7195 if (!res)
7196 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
7197 }
7198 } else {
7199 if (tm.tm_hour == 0) {
7200 if (format[offset] == 'I')
7201 res = wait_file(chan, ints, "digits/pt-ah", lang);
7202 if (!res)
7203 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
7204 }
7205 else if (tm.tm_hour == 12) {
7206 if (format[offset] == 'I')
7207 res = wait_file(chan, ints, "digits/pt-ao", lang);
7208 if (!res)
7209 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
7210 }
7211 else {
7212 if (format[offset] == 'I') {
7213 res = wait_file(chan, ints, "digits/pt-ah", lang);
7214 if ((tm.tm_hour % 12) != 1)
7215 if (!res)
7216 res = wait_file(chan, ints, "digits/pt-sss", lang);
7217 }
7218 if (!res)
7219 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
7220 }
7221 }
7222 break;
7223 case 'H':
7224 case 'k':
7225 /* 24-Hour */
7226 if (!strcasecmp(lang, "pt_BR")) {
7227 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7228 if ((!res) && (format[offset] == 'H')) {
7229 if (tm.tm_hour > 1) {
7230 res = wait_file(chan, ints, "digits/hours", lang);
7231 } else {
7232 res = wait_file(chan, ints, "digits/hour", lang);
7233 }
7234 }
7235 } else {
7236 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
7237 if (!res) {
7238 if (tm.tm_hour != 0) {
7239 int remaining = tm.tm_hour;
7240 if (tm.tm_hour > 20) {
7241 res = wait_file(chan, ints, "digits/20", lang);
7242 remaining -= 20;
7243 }
7244 if (!res) {
7245 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
7246 res = wait_file(chan, ints, nextmsg, lang);
7247 }
7248 }
7249 }
7250 }
7251 break;
7252 case 'M':
7253 /* Minute */
7254 if (!strcasecmp(lang, "pt_BR")) {
7255 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
7256 if (!res) {
7257 if (tm.tm_min > 1) {
7258 res = wait_file(chan, ints, "minutes", lang);
7259 } else {
7260 res = wait_file(chan, ints, "minute", lang);
7261 }
7262 }
7263 } else {
7264 if (tm.tm_min == 0) {
7265 res = wait_file(chan, ints, "digits/pt-hora", lang);
7266 if (tm.tm_hour != 1)
7267 if (!res)
7268 res = wait_file(chan, ints, "digits/pt-sss", lang);
7269 } else {
7270 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7271 }
7272 }
7273 break;
7274 case 'P':
7275 case 'p':
7276 /* AM/PM */
7277 if (!strcasecmp(lang, "pt_BR")) {
7278 if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
7279 res = wait_file(chan, ints, "digits/pt-da", lang);
7280 if (!res) {
7281 if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
7282 res = wait_file(chan, ints, "digits/morning", lang);
7283 else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
7284 res = wait_file(chan, ints, "digits/afternoon", lang);
7285 else res = wait_file(chan, ints, "digits/night", lang);
7286 }
7287 }
7288 } else {
7289 if (tm.tm_hour > 12)
7290 res = wait_file(chan, ints, "digits/p-m", lang);
7291 else if (tm.tm_hour && tm.tm_hour < 12)
7292 res = wait_file(chan, ints, "digits/a-m", lang);
7293 }
7294 break;
7295 case 'Q':
7296 /* Shorthand for "Today", "Yesterday", or ABdY */
7297 /* XXX As emphasized elsewhere, this should the native way in your
7298 * language to say the date, with changes in what you say, depending
7299 * upon how recent the date is. XXX */
7300 {
7301 struct timeval now = ast_tvnow();
7302 struct ast_tm tmnow;
7303 time_t beg_today;
7304
7305 ast_localtime(&now, &tmnow, tzone);
7306 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7307 /* In any case, it saves not having to do ast_mktime() */
7308 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7309 if (beg_today < t) {
7310 /* Today */
7311 res = wait_file(chan, ints, "digits/today", lang);
7312 } else if (beg_today - 86400 < t) {
7313 /* Yesterday */
7314 res = wait_file(chan, ints, "digits/yesterday", lang);
7315 } else {
7316 res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
7317 }
7318 }
7319 break;
7320 case 'q':
7321 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
7322 /* XXX As emphasized elsewhere, this should the native way in your
7323 * language to say the date, with changes in what you say, depending
7324 * upon how recent the date is. XXX */
7325 {
7326 struct timeval now = ast_tvnow();
7327 struct ast_tm tmnow;
7328 time_t beg_today;
7329
7330 ast_localtime(&now, &tmnow, tzone);
7331 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7332 /* In any case, it saves not having to do ast_mktime() */
7333 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7334 if (beg_today < t) {
7335 /* Today */
7336 } else if ((beg_today - 86400) < t) {
7337 /* Yesterday */
7338 res = wait_file(chan, ints, "digits/yesterday", lang);
7339 } else if (beg_today - 86400 * 6 < t) {
7340 /* Within the last week */
7341 res = ast_say_date_with_format_pt(chan, t, ints, lang, "A", tzone);
7342 } else {
7343 res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
7344 }
7345 }
7346 break;
7347 case 'R':
7348 res = ast_say_date_with_format_pt(chan, t, ints, lang, "H 'digits/pt-e' M", tzone);
7349 break;
7350 case 'S':
7351 /* Seconds */
7352 if (!strcasecmp(lang, "pt_BR")) {
7353 res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
7354 if (!res) {
7355 if (tm.tm_sec > 1) {
7356 res = wait_file(chan, ints, "seconds", lang);
7357 } else {
7358 res = wait_file(chan, ints, "second", lang);
7359 }
7360 }
7361 } else {
7362 if (tm.tm_sec == 0) {
7363 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7364 res = wait_file(chan, ints, nextmsg, lang);
7365 } else if (tm.tm_sec < 10) {
7366 res = wait_file(chan, ints, "digits/oh", lang);
7367 if (!res) {
7368 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7369 res = wait_file(chan, ints, nextmsg, lang);
7370 }
7371 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
7372 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7373 res = wait_file(chan, ints, nextmsg, lang);
7374 } else {
7375 int ten, one;
7376 ten = (tm.tm_sec / 10) * 10;
7377 one = (tm.tm_sec % 10);
7378 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
7379 res = wait_file(chan, ints, nextmsg, lang);
7380 if (!res) {
7381 /* Fifty, not fifty-zero */
7382 if (one != 0) {
7383 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
7384 res = wait_file(chan, ints, nextmsg, lang);
7385 }
7386 }
7387 }
7388 }
7389 break;
7390 case 'T':
7391 res = ast_say_date_with_format_pt(chan, t, ints, lang, "HMS", tzone);
7392 break;
7393 case ' ':
7394 case ' ':
7395 /* Just ignore spaces and tabs */
7396 break;
7397 default:
7398 /* Unknown character */
7399 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
7400 }
7401 /* Jump out on DTMF */
7402 if (res) {
7403 break;
7404 }
7405 }
7406 return res;
7407}
7408
7409/*! \brief Taiwanese / Chinese syntax */
7410int 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)
7411{
7412 struct timeval when = { t, 0 };
7413 struct ast_tm tm;
7414 int res=0, offset, sndoffset;
7415 char sndfile[256], nextmsg[256];
7416
7417 if (format == NULL)
7418 format = "YBdAkM";
7419
7420 ast_localtime(&when, &tm, tzone);
7421
7422 for (offset=0 ; format[offset] != '\0' ; offset++) {
7423 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
7424 switch (format[offset]) {
7425 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
7426 case '\'':
7427 /* Literal name of a sound file */
7428 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
7429 sndfile[sndoffset] = format[offset];
7430 }
7431 sndfile[sndoffset] = '\0';
7432 res = wait_file(chan, ints, sndfile, lang);
7433 break;
7434 case 'A':
7435 case 'a':
7436 /* Sunday - Saturday */
7437 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
7438 res = wait_file(chan, ints, nextmsg, lang);
7439 break;
7440 case 'B':
7441 case 'b':
7442 case 'h':
7443 case 'm':
7444 /* January - December */
7445 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
7446 res = wait_file(chan, ints, nextmsg, lang);
7447 break;
7448 case 'd':
7449 case 'e':
7450 /* First - Thirtyfirst */
7451 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
7452 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday);
7453 res = wait_file(chan, ints, nextmsg, lang);
7454 } else {
7455 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
7456 res = wait_file(chan, ints, nextmsg, lang);
7457 if (!res) {
7458 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
7459 res = wait_file(chan, ints, nextmsg, lang);
7460 }
7461 }
7462 if (!res) res = wait_file(chan, ints, "digits/day", lang);
7463 break;
7464 case 'Y':
7465 /* Year */
7466 if (tm.tm_year > 99) {
7467 res = wait_file(chan, ints, "digits/2", lang);
7468 if (!res) {
7469 res = wait_file(chan, ints, "digits/thousand", lang);
7470 }
7471 if (tm.tm_year > 100) {
7472 if (!res) {
7473 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
7474 res = wait_file(chan, ints, nextmsg, lang);
7475 if (!res) {
7476 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
7477 res = wait_file(chan, ints, nextmsg, lang);
7478 }
7479 }
7480 }
7481 if (!res) {
7482 res = wait_file(chan, ints, "digits/year", lang);
7483 }
7484 } else {
7485 if (tm.tm_year < 1) {
7486 /* I'm not going to handle 1900 and prior */
7487 /* We'll just be silent on the year, instead of bombing out. */
7488 } else {
7489 res = wait_file(chan, ints, "digits/1", lang);
7490 if (!res) {
7491 res = wait_file(chan, ints, "digits/9", lang);
7492 }
7493 if (!res) {
7494 if (tm.tm_year <= 9) {
7495 /* 1901 - 1909 */
7496 res = wait_file(chan, ints, "digits/0", lang);
7497 if (!res) {
7498 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
7499 res = wait_file(chan, ints, nextmsg, lang);
7500 }
7501 } else {
7502 /* 1910 - 1999 */
7503 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
7504 res = wait_file(chan, ints, nextmsg, lang);
7505 if (!res) {
7506 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
7507 res = wait_file(chan, ints, nextmsg, lang);
7508 }
7509 }
7510 }
7511 }
7512 if (!res) {
7513 res = wait_file(chan, ints, "digits/year", lang);
7514 }
7515 }
7516 break;
7517 case 'I':
7518 case 'l':
7519 /* 12-Hour */
7520 if (tm.tm_hour == 0)
7521 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
7522 else if (tm.tm_hour > 12)
7523 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
7524 else
7525 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
7526 res = wait_file(chan, ints, nextmsg, lang);
7527 if (!res) {
7528 res = wait_file(chan, ints, "digits/oclock", lang);
7529 }
7530 break;
7531 case 'H':
7532 if (tm.tm_hour < 10) {
7533 res = wait_file(chan, ints, "digits/0", lang);
7534 }
7535 /* XXX Static analysis warns of no break here. No idea if this is
7536 * correct or not
7537 */
7538 case 'k':
7539 /* 24-Hour */
7540 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
7541 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
7542 res = wait_file(chan, ints, nextmsg, lang);
7543 } else {
7544 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
7545 res = wait_file(chan, ints, nextmsg, lang);
7546 if (!res) {
7547 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
7548 res = wait_file(chan, ints, nextmsg, lang);
7549 }
7550 }
7551 if (!res) {
7552 res = wait_file(chan, ints, "digits/oclock", lang);
7553 }
7554 break;
7555 case 'M':
7556 /* Minute */
7557 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
7558 if (tm.tm_min < 10) {
7559 res = wait_file(chan, ints, "digits/0", lang);
7560 }
7561 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
7562 res = wait_file(chan, ints, nextmsg, lang);
7563 } else {
7564 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
7565 res = wait_file(chan, ints, nextmsg, lang);
7566 if (!res) {
7567 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
7568 res = wait_file(chan, ints, nextmsg, lang);
7569 }
7570 }
7571 if (!res) {
7572 res = wait_file(chan, ints, "minute", lang);
7573 }
7574 break;
7575 case 'P':
7576 case 'p':
7577 /* AM/PM */
7578 if (tm.tm_hour > 11)
7579 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
7580 else
7581 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
7582 res = wait_file(chan, ints, nextmsg, lang);
7583 break;
7584 case 'Q':
7585 /* Shorthand for "Today", "Yesterday", or ABdY */
7586 /* XXX As emphasized elsewhere, this should the native way in your
7587 * language to say the date, with changes in what you say, depending
7588 * upon how recent the date is. XXX */
7589 {
7590 struct timeval now = ast_tvnow();
7591 struct ast_tm tmnow;
7592 time_t beg_today;
7593
7594 ast_localtime(&now, &tmnow, tzone);
7595 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7596 /* In any case, it saves not having to do ast_mktime() */
7597 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7598 if (beg_today < t) {
7599 /* Today */
7600 res = wait_file(chan, ints, "digits/today", lang);
7601 } else if (beg_today - 86400 < t) {
7602 /* Yesterday */
7603 res = wait_file(chan, ints, "digits/yesterday", lang);
7604 } else {
7605 res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
7606 }
7607 }
7608 break;
7609 case 'q':
7610 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
7611 /* XXX As emphasized elsewhere, this should the native way in your
7612 * language to say the date, with changes in what you say, depending
7613 * upon how recent the date is. XXX */
7614 {
7615 struct timeval now = ast_tvnow();
7616 struct ast_tm tmnow;
7617 time_t beg_today;
7618
7619 ast_localtime(&now, &tmnow, tzone);
7620 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7621 /* In any case, it saves not having to do ast_mktime() */
7622 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7623 if (beg_today < t) {
7624 /* Today */
7625 } else if ((beg_today - 86400) < t) {
7626 /* Yesterday */
7627 res = wait_file(chan, ints, "digits/yesterday", lang);
7628 } else if (beg_today - 86400 * 6 < t) {
7629 /* Within the last week */
7630 res = ast_say_date_with_format_zh(chan, t, ints, lang, "A", tzone);
7631 } else {
7632 res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
7633 }
7634 }
7635 break;
7636 case 'R':
7637 res = ast_say_date_with_format_zh(chan, t, ints, lang, "kM", tzone);
7638 break;
7639 case 'S':
7640 /* Seconds */
7641 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
7642 if (tm.tm_sec < 10) {
7643 res = wait_file(chan, ints, "digits/0", lang);
7644 }
7645 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7646 res = wait_file(chan, ints, nextmsg, lang);
7647 } else {
7648 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
7649 res = wait_file(chan, ints, nextmsg, lang);
7650 if (!res) {
7651 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
7652 res = wait_file(chan, ints, nextmsg, lang);
7653 }
7654 }
7655 if (!res) {
7656 res = wait_file(chan, ints, "second", lang);
7657 }
7658 break;
7659 case 'T':
7660 res = ast_say_date_with_format_zh(chan, t, ints, lang, "HMS", tzone);
7661 break;
7662 case ' ':
7663 case ' ':
7664 /* Just ignore spaces and tabs */
7665 break;
7666 default:
7667 /* Unknown character */
7668 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
7669 }
7670 /* Jump out on DTMF */
7671 if (res) {
7672 break;
7673 }
7674 }
7675 return res;
7676}
7677
7678static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7679{
7680 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7681 return ast_say_time_en(chan, t, ints, lang);
7682 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7683 return ast_say_time_de(chan, t, ints, lang);
7684 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7685 return ast_say_time_fr(chan, t, ints, lang);
7686 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7687 return ast_say_time_gr(chan, t, ints, lang);
7688 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7689 return ast_say_time_ja(chan, t, ints, lang);
7690 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7691 return ast_say_time_he(chan, t, ints, lang);
7692 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7693 return(ast_say_time_hu(chan, t, ints, lang));
7694 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7695 return ast_say_time_ka(chan, t, ints, lang);
7696 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7697 return ast_say_time_nl(chan, t, ints, lang);
7698 } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7699 return ast_say_time_pt_BR(chan, t, ints, lang);
7700 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7701 return ast_say_time_pt(chan, t, ints, lang);
7702 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7703 return(ast_say_time_th(chan, t, ints, lang));
7704 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7705 return ast_say_time_zh(chan, t, ints, lang);
7706 } else if (!strncasecmp(lang, "ps", 2)) { /* Pashto syntax */
7707 return ast_say_time_en(chan, t, ints, lang);
7708 } else if (!strncasecmp(lang, "dr", 2)) { /* Dari syntax */
7709 return ast_say_time_en(chan, t, ints, lang);
7710 }
7711
7712 /* Default to English */
7713 return ast_say_time_en(chan, t, ints, lang);
7714}
7715
7716/*! \brief English syntax */
7717int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7718{
7719 struct timeval when = { t, 0 };
7720 struct ast_tm tm;
7721 int res = 0;
7722 int hour, pm=0;
7723
7724 ast_localtime(&when, &tm, NULL);
7725 hour = tm.tm_hour;
7726 if (!hour)
7727 hour = 12;
7728 else if (hour == 12)
7729 pm = 1;
7730 else if (hour > 12) {
7731 hour -= 12;
7732 pm = 1;
7733 }
7734 if (!res)
7735 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7736
7737 if (tm.tm_min > 9) {
7738 if (!res)
7739 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7740 } else if (tm.tm_min) {
7741 if (!res)
7742 res = ast_streamfile(chan, "digits/oh", lang);
7743 if (!res)
7744 res = ast_waitstream(chan, ints);
7745 if (!res)
7746 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7747 } else {
7748 if (!res)
7749 res = ast_streamfile(chan, "digits/oclock", lang);
7750 if (!res)
7751 res = ast_waitstream(chan, ints);
7752 }
7753 if (pm) {
7754 if (!res)
7755 res = ast_streamfile(chan, "digits/p-m", lang);
7756 } else {
7757 if (!res)
7758 res = ast_streamfile(chan, "digits/a-m", lang);
7759 }
7760 if (!res)
7761 res = ast_waitstream(chan, ints);
7762 return res;
7763}
7764
7765/*! \brief German syntax */
7766int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7767{
7768 struct timeval when = { t, 0 };
7769 struct ast_tm tm;
7770 int res = 0;
7771
7772 ast_localtime(&when, &tm, NULL);
7773 if (!res)
7774 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7775 if (!res)
7776 res = ast_streamfile(chan, "digits/oclock", lang);
7777 if (!res)
7778 res = ast_waitstream(chan, ints);
7779 if (!res)
7780 if (tm.tm_min > 0)
7781 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7782 return res;
7783}
7784
7785/*! \brief Hungarian syntax */
7786int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7787{
7788 struct timeval when = { t, 0 };
7789 struct ast_tm tm;
7790 int res = 0;
7791
7792 ast_localtime(&when, &tm, NULL);
7793 if (!res)
7794 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7795 if (!res)
7796 res = ast_streamfile(chan, "digits/oclock", lang);
7797 if (!res)
7798 res = ast_waitstream(chan, ints);
7799 if (!res)
7800 if (tm.tm_min > 0) {
7801 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7802 if (!res)
7803 res = ast_streamfile(chan, "minute", lang);
7804 }
7805 return res;
7806}
7807
7808/*! \brief French syntax */
7809int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7810{
7811 struct timeval when = { t, 0 };
7812 struct ast_tm tm;
7813 int res = 0;
7814
7815 ast_localtime(&when, &tm, NULL);
7816
7817 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7818 if (!res) {
7819 res = ast_streamfile(chan, "digits/oclock", lang);
7820 }
7821 if (!res) {
7822 res = ast_waitstream(chan, ints);
7823 }
7824 if (tm.tm_min) {
7825 if (!res) {
7826 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7827 }
7828 }
7829 return res;
7830}
7831
7832/*! \brief Dutch syntax */
7833int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7834{
7835 struct timeval when = { t, 0 };
7836 struct ast_tm tm;
7837 int res = 0;
7838
7839 ast_localtime(&when, &tm, NULL);
7840 if (!res)
7841 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
7842 if (!res)
7843 res = ast_streamfile(chan, "digits/nl-uur", lang);
7844 if (!res)
7845 res = ast_waitstream(chan, ints);
7846 if (!res)
7847 if (tm.tm_min > 0)
7848 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
7849 return res;
7850}
7851
7852/*! \brief Portuguese syntax */
7853int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7854{
7855 struct timeval when = { t, 0 };
7856 struct ast_tm tm;
7857 int res = 0;
7858 int hour;
7859
7860 ast_localtime(&when, &tm, NULL);
7861 hour = tm.tm_hour;
7862 if (!res)
7863 res = ast_say_number(chan, hour, ints, lang, "f");
7864 if (tm.tm_min) {
7865 if (!res)
7866 res = wait_file(chan, ints, "digits/pt-e", lang);
7867 if (!res)
7868 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7869 } else {
7870 if (!res)
7871 res = wait_file(chan, ints, "digits/pt-hora", lang);
7872 if (tm.tm_hour != 1)
7873 if (!res)
7874 res = wait_file(chan, ints, "digits/pt-sss", lang);
7875 }
7876 if (!res)
7877 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7878 return res;
7879}
7880
7881/*! \brief Brazilian Portuguese syntax */
7882int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7883{
7884 struct timeval when = { t, 0 };
7885 struct ast_tm tm;
7886 int res = 0;
7887
7888 ast_localtime(&when, &tm, NULL);
7889
7890 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7891 if (!res) {
7892 if (tm.tm_hour > 1)
7893 res = wait_file(chan, ints, "digits/hours", lang);
7894 else
7895 res = wait_file(chan, ints, "digits/hour", lang);
7896 }
7897 if ((!res) && (tm.tm_min)) {
7898 res = wait_file(chan, ints, "digits/pt-e", lang);
7899 if (!res)
7900 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7901 if (!res) {
7902 if (tm.tm_min > 1)
7903 res = wait_file(chan, ints, "minutes", lang);
7904 else
7905 res = wait_file(chan, ints, "minute", lang);
7906 }
7907 }
7908 return res;
7909}
7910
7911/*! \brief Thai syntax */
7912int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7913{
7914 struct timeval when = { t, 0 };
7915 struct ast_tm tm;
7916 int res = 0;
7917 int hour;
7918 ast_localtime(&when, &tm, NULL);
7919 hour = tm.tm_hour;
7920 if (!hour)
7921 hour = 24;
7922 if (!res)
7923 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7924 if (!res)
7925 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7926 return res;
7927}
7928
7929/*! \brief Taiwanese / Chinese syntax */
7930int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7931{
7932 struct timeval when = { t, 0 };
7933 struct ast_tm tm;
7934 int res = 0;
7935 int hour, pm=0;
7936
7937 ast_localtime(&when, &tm, NULL);
7938 hour = tm.tm_hour;
7939 if (!hour)
7940 hour = 12;
7941 else if (hour == 12)
7942 pm = 1;
7943 else if (hour > 12) {
7944 hour -= 12;
7945 pm = 1;
7946 }
7947 if (pm) {
7948 if (!res)
7949 res = ast_streamfile(chan, "digits/p-m", lang);
7950 } else {
7951 if (!res)
7952 res = ast_streamfile(chan, "digits/a-m", lang);
7953 }
7954 if (!res)
7955 res = ast_waitstream(chan, ints);
7956 if (!res)
7957 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7958 if (!res)
7959 res = ast_streamfile(chan, "digits/oclock", lang);
7960 if (!res)
7961 res = ast_waitstream(chan, ints);
7962 if (!res)
7963 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7964 if (!res)
7965 res = ast_streamfile(chan, "minute", lang);
7966 if (!res)
7967 res = ast_waitstream(chan, ints);
7968 return res;
7969}
7970
7971/*! \brief Hebrew syntax */
7972int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7973{
7974 struct timeval when = { t, 0 };
7975 struct ast_tm tm;
7976 int res = 0;
7977 int hour;
7978
7979 ast_localtime(&when, &tm, NULL);
7980 hour = tm.tm_hour;
7981 if (!hour)
7982 hour = 12;
7983
7984 if (!res)
7985 res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
7986
7987 if (tm.tm_min > 9) {
7988 if (!res)
7989 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7990 } else if (tm.tm_min) {
7991 if (!res) { /* say a leading zero if needed */
7992 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
7993 }
7994 if (!res)
7995 res = ast_waitstream(chan, ints);
7996 if (!res)
7997 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7998 } else {
7999 if (!res)
8000 res = ast_waitstream(chan, ints);
8001 }
8002 if (!res)
8003 res = ast_waitstream(chan, ints);
8004 return res;
8005}
8006
8007
8008static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8009{
8010 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
8011 return ast_say_datetime_en(chan, t, ints, lang);
8012 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
8013 return ast_say_datetime_de(chan, t, ints, lang);
8014 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
8015 return ast_say_datetime_fr(chan, t, ints, lang);
8016 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
8017 return ast_say_datetime_gr(chan, t, ints, lang);
8018 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
8019 return ast_say_datetime_ja(chan, t, ints, lang);
8020 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
8021 return ast_say_datetime_he(chan, t, ints, lang);
8022 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
8023 return ast_say_datetime_hu(chan, t, ints, lang);
8024 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
8025 return ast_say_datetime_ka(chan, t, ints, lang);
8026 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
8027 return ast_say_datetime_nl(chan, t, ints, lang);
8028 } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
8029 return ast_say_datetime_pt_BR(chan, t, ints, lang);
8030 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
8031 return ast_say_datetime_pt(chan, t, ints, lang);
8032 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
8033 return ast_say_datetime_th(chan, t, ints, lang);
8034 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
8035 return ast_say_datetime_zh(chan, t, ints, lang);
8036 }
8037
8038 /* Default to English */
8039 return ast_say_datetime_en(chan, t, ints, lang);
8040}
8041
8042/*! \brief English syntax */
8043int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8044{
8045 struct timeval when = { t, 0 };
8046 struct ast_tm tm;
8047 char fn[256];
8048 int res = 0;
8049 int hour, pm=0;
8050
8051 ast_localtime(&when, &tm, NULL);
8052 if (!res) {
8053 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8054 res = ast_streamfile(chan, fn, lang);
8055 if (!res)
8056 res = ast_waitstream(chan, ints);
8057 }
8058 if (!res) {
8059 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8060 res = ast_streamfile(chan, fn, lang);
8061 if (!res)
8062 res = ast_waitstream(chan, ints);
8063 }
8064 if (!res)
8065 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8066
8067 hour = tm.tm_hour;
8068 if (!hour)
8069 hour = 12;
8070 else if (hour == 12)
8071 pm = 1;
8072 else if (hour > 12) {
8073 hour -= 12;
8074 pm = 1;
8075 }
8076 if (!res)
8077 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8078
8079 if (tm.tm_min > 9) {
8080 if (!res)
8081 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8082 } else if (tm.tm_min) {
8083 if (!res)
8084 res = ast_streamfile(chan, "digits/oh", lang);
8085 if (!res)
8086 res = ast_waitstream(chan, ints);
8087 if (!res)
8088 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8089 } else {
8090 if (!res)
8091 res = ast_streamfile(chan, "digits/oclock", lang);
8092 if (!res)
8093 res = ast_waitstream(chan, ints);
8094 }
8095 if (pm) {
8096 if (!res)
8097 res = ast_streamfile(chan, "digits/p-m", lang);
8098 } else {
8099 if (!res)
8100 res = ast_streamfile(chan, "digits/a-m", lang);
8101 }
8102 if (!res)
8103 res = ast_waitstream(chan, ints);
8104 if (!res)
8105 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8106 return res;
8107}
8108
8109/*! \brief German syntax */
8110int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8111{
8112 struct timeval when = { t, 0 };
8113 struct ast_tm tm;
8114 int res = 0;
8115
8116 ast_localtime(&when, &tm, NULL);
8117 res = ast_say_date(chan, t, ints, lang);
8118 if (!res)
8119 ast_say_time(chan, t, ints, lang);
8120 return res;
8121
8122}
8123
8124/*! \brief Hungarian syntax */
8125int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8126{
8127 struct timeval when = { t, 0 };
8128 struct ast_tm tm;
8129 int res = 0;
8130
8131 ast_localtime(&when, &tm, NULL);
8132 res = ast_say_date(chan, t, ints, lang);
8133 if (!res)
8134 ast_say_time(chan, t, ints, lang);
8135 return res;
8136}
8137
8138/*! \brief French syntax */
8139int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8140{
8141 struct timeval when = { t, 0 };
8142 struct ast_tm tm;
8143 char fn[256];
8144 int res = 0;
8145
8146 ast_localtime(&when, &tm, NULL);
8147
8148 if (!res)
8149 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8150
8151 if (!res) {
8152 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8153 res = ast_streamfile(chan, fn, lang);
8154 if (!res)
8155 res = ast_waitstream(chan, ints);
8156 }
8157 if (!res) {
8158 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8159 res = ast_streamfile(chan, fn, lang);
8160 if (!res)
8161 res = ast_waitstream(chan, ints);
8162 }
8163
8164 if (!res)
8165 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
8166 if (!res)
8167 res = ast_streamfile(chan, "digits/oclock", lang);
8168 if (tm.tm_min > 0) {
8169 if (!res)
8170 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8171 }
8172 if (!res)
8173 res = ast_waitstream(chan, ints);
8174 if (!res)
8175 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8176 return res;
8177}
8178
8179/*! \brief Dutch syntax */
8180int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8181{
8182 struct timeval when = { t, 0 };
8183 struct ast_tm tm;
8184 int res = 0;
8185
8186 ast_localtime(&when, &tm, NULL);
8187 res = ast_say_date(chan, t, ints, lang);
8188 if (!res) {
8189 res = ast_streamfile(chan, "digits/nl-om", lang);
8190 if (!res)
8191 res = ast_waitstream(chan, ints);
8192 }
8193 if (!res)
8194 ast_say_time(chan, t, ints, lang);
8195 return res;
8196}
8197
8198/*! \brief Portuguese syntax */
8199int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8200{
8201 struct timeval when = { t, 0 };
8202 struct ast_tm tm;
8203 char fn[256];
8204 int res = 0;
8205 int hour, pm=0;
8206
8207 ast_localtime(&when, &tm, NULL);
8208 if (!res) {
8209 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8210 res = ast_streamfile(chan, fn, lang);
8211 if (!res)
8212 res = ast_waitstream(chan, ints);
8213 }
8214 if (!res) {
8215 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8216 res = ast_streamfile(chan, fn, lang);
8217 if (!res)
8218 res = ast_waitstream(chan, ints);
8219 }
8220 if (!res)
8221 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8222
8223 hour = tm.tm_hour;
8224 if (!hour)
8225 hour = 12;
8226 else if (hour == 12)
8227 pm = 1;
8228 else if (hour > 12) {
8229 hour -= 12;
8230 pm = 1;
8231 }
8232 if (!res)
8233 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8234
8235 if (tm.tm_min > 9) {
8236 if (!res)
8237 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8238 } else if (tm.tm_min) {
8239 if (!res)
8240 res = ast_streamfile(chan, "digits/oh", lang);
8241 if (!res)
8242 res = ast_waitstream(chan, ints);
8243 if (!res)
8244 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8245 } else {
8246 if (!res)
8247 res = ast_streamfile(chan, "digits/oclock", lang);
8248 if (!res)
8249 res = ast_waitstream(chan, ints);
8250 }
8251 if (pm) {
8252 if (!res)
8253 res = ast_streamfile(chan, "digits/p-m", lang);
8254 } else {
8255 if (!res)
8256 res = ast_streamfile(chan, "digits/a-m", lang);
8257 }
8258 if (!res)
8259 res = ast_waitstream(chan, ints);
8260 if (!res)
8261 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8262 return res;
8263}
8264
8265/*! \brief Brazilian Portuguese syntax */
8266int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8267{
8268 struct timeval when = { t, 0 };
8269 struct ast_tm tm;
8270 int res = 0;
8271
8272 ast_localtime(&when, &tm, NULL);
8273 res = ast_say_date(chan, t, ints, lang);
8274 if (!res)
8275 res = ast_say_time(chan, t, ints, lang);
8276 return res;
8277}
8278
8279/*! \brief Thai syntax */
8280int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8281{
8282 struct timeval when = { t, 0 };
8283 struct ast_tm tm;
8284 char fn[256];
8285 int res = 0;
8286 int hour;
8287 ast_localtime(&when, &tm, NULL);
8288 if (!res) {
8289 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8290 res = ast_streamfile(chan, fn, lang);
8291 if (!res)
8292 res = ast_waitstream(chan, ints);
8293 }
8294 if (!res) {
8295 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8296 res = ast_streamfile(chan, fn, lang);
8297 if (!res)
8298 res = ast_waitstream(chan, ints);
8299 }
8300 if (!res){
8301 ast_copy_string(fn, "digits/posor", sizeof(fn));
8302 res = ast_streamfile(chan, fn, lang);
8303 res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
8304 }
8305 if (!res)
8306 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8307
8308 hour = tm.tm_hour;
8309 if (!hour)
8310 hour = 24;
8311 if (!res){
8312 ast_copy_string(fn, "digits/wela", sizeof(fn));
8313 res = ast_streamfile(chan, fn, lang);
8314 }
8315 if (!res)
8316 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8317 if (!res)
8318 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8319 return res;
8320}
8321
8322/*! \brief Taiwanese / Chinese syntax */
8323int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8324{
8325 struct timeval when = { t, 0 };
8326 struct ast_tm tm;
8327 char fn[256];
8328 int res = 0;
8329 int hour, pm=0;
8330
8331 ast_localtime(&when, &tm, NULL);
8332 if (!res)
8333 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8334 if (!res) {
8335 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8336 res = ast_streamfile(chan, fn, lang);
8337 if (!res)
8338 res = ast_waitstream(chan, ints);
8339 }
8340 if (!res)
8341 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8342 if (!res) {
8343 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8344 res = ast_streamfile(chan, fn, lang);
8345 if (!res)
8346 res = ast_waitstream(chan, ints);
8347 }
8348
8349 hour = tm.tm_hour;
8350 if (!hour)
8351 hour = 12;
8352 else if (hour == 12)
8353 pm = 1;
8354 else if (hour > 12) {
8355 hour -= 12;
8356 pm = 1;
8357 }
8358 if (pm) {
8359 if (!res)
8360 res = ast_streamfile(chan, "digits/p-m", lang);
8361 } else {
8362 if (!res)
8363 res = ast_streamfile(chan, "digits/a-m", lang);
8364 }
8365 if (!res)
8366 res = ast_waitstream(chan, ints);
8367 if (!res)
8368 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8369 if (!res)
8370 res = ast_streamfile(chan, "digits/oclock", lang);
8371 if (!res)
8372 res = ast_waitstream(chan, ints);
8373 if (!res)
8374 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8375 if (!res)
8376 res = ast_streamfile(chan, "minute", lang);
8377 if (!res)
8378 res = ast_waitstream(chan, ints);
8379 return res;
8380}
8381
8382/*! \brief Hebrew syntax */
8383int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8384{
8385 struct timeval when = { t, 0 };
8386 struct ast_tm tm;
8387 char fn[256];
8388 int res = 0;
8389 int hour;
8390
8391 ast_localtime(&when, &tm, NULL);
8392 if (!res) {
8393 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8394 res = ast_streamfile(chan, fn, lang);
8395 if (!res) {
8396 res = ast_waitstream(chan, ints);
8397 }
8398 }
8399 if (!res) {
8400 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8401 res = ast_streamfile(chan, fn, lang);
8402 if (!res) {
8403 res = ast_waitstream(chan, ints);
8404 }
8405 }
8406 if (!res) {
8407 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
8408 }
8409
8410 hour = tm.tm_hour;
8411 if (!hour) {
8412 hour = 12;
8413 }
8414
8415 if (!res) {
8416 res = ast_say_number(chan, hour, ints, lang, "f");
8417 }
8418
8419 if (tm.tm_min > 9) {
8420 if (!res) {
8421 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
8422 }
8423 } else if (tm.tm_min) {
8424 if (!res) {
8425 /* say a leading zero if needed */
8426 res = ast_say_number(chan, 0, ints, lang, "f");
8427 }
8428 if (!res) {
8429 res = ast_waitstream(chan, ints);
8430 }
8431 if (!res) {
8432 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
8433 }
8434 } else {
8435 if (!res) {
8436 res = ast_waitstream(chan, ints);
8437 }
8438 }
8439 if (!res) {
8440 res = ast_waitstream(chan, ints);
8441 }
8442 if (!res) {
8443 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
8444 }
8445 return res;
8446}
8447
8448static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8449{
8450 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
8451 return ast_say_datetime_from_now_en(chan, t, ints, lang);
8452 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
8453 return ast_say_datetime_from_now_fr(chan, t, ints, lang);
8454 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
8455 return ast_say_datetime_from_now_he(chan, t, ints, lang);
8456 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
8457 return ast_say_datetime_from_now_ka(chan, t, ints, lang);
8458 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
8459 return ast_say_datetime_from_now_pt(chan, t, ints, lang);
8460 }
8461
8462 /* Default to English */
8463 return ast_say_datetime_from_now_en(chan, t, ints, lang);
8464}
8465
8466/*! \brief English syntax */
8467int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8468{
8469 int res=0;
8470 struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8471 int daydiff;
8472 struct ast_tm tm;
8473 struct ast_tm now;
8474 char fn[256];
8475
8476 ast_localtime(&when, &tm, NULL);
8477 ast_localtime(&nowtv, &now, NULL);
8478 daydiff = now.tm_yday - tm.tm_yday;
8479 if ((daydiff < 0) || (daydiff > 6)) {
8480 /* Day of month and month */
8481 if (!res) {
8482 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8483 res = ast_streamfile(chan, fn, lang);
8484 if (!res)
8485 res = ast_waitstream(chan, ints);
8486 }
8487 if (!res)
8488 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8489
8490 } else if (daydiff) {
8491 /* Just what day of the week */
8492 if (!res) {
8493 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8494 res = ast_streamfile(chan, fn, lang);
8495 if (!res)
8496 res = ast_waitstream(chan, ints);
8497 }
8498 } /* Otherwise, it was today */
8499 if (!res)
8500 res = ast_say_time(chan, t, ints, lang);
8501 return res;
8502}
8503
8504/*! \brief French syntax */
8505int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8506{
8507 int res=0;
8508 struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8509 int daydiff;
8510 struct ast_tm tm;
8511 struct ast_tm now;
8512 char fn[256];
8513
8514 ast_localtime(&when, &tm, NULL);
8515 ast_localtime(&nowtv, &now, NULL);
8516 daydiff = now.tm_yday - tm.tm_yday;
8517 if ((daydiff < 0) || (daydiff > 6)) {
8518 /* Day of month and month */
8519 if (!res) {
8520 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8521 res = ast_streamfile(chan, fn, lang);
8522 if (!res)
8523 res = ast_waitstream(chan, ints);
8524 }
8525 if (!res)
8526 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8527
8528 } else if (daydiff) {
8529 /* Just what day of the week */
8530 if (!res) {
8531 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8532 res = ast_streamfile(chan, fn, lang);
8533 if (!res)
8534 res = ast_waitstream(chan, ints);
8535 }
8536 } /* Otherwise, it was today */
8537 if (!res)
8538 res = ast_say_time(chan, t, ints, lang);
8539 return res;
8540}
8541
8542/*! \brief Portuguese syntax */
8543int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8544{
8545 int res=0;
8546 int daydiff;
8547 struct ast_tm tm;
8548 struct ast_tm now;
8549 struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8550 char fn[256];
8551
8552 ast_localtime(&when, &tm, NULL);
8553 ast_localtime(&nowtv, &now, NULL);
8554 daydiff = now.tm_yday - tm.tm_yday;
8555 if ((daydiff < 0) || (daydiff > 6)) {
8556 /* Day of month and month */
8557 if (!res)
8558 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8559 if (!res)
8560 res = wait_file(chan, ints, "digits/pt-de", lang);
8561 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8562 if (!res)
8563 res = wait_file(chan, ints, fn, lang);
8564
8565 } else if (daydiff) {
8566 /* Just what day of the week */
8567 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8568 if (!res)
8569 res = wait_file(chan, ints, fn, lang);
8570 } /* Otherwise, it was today */
8571 if (!strcasecmp(lang, "pt_BR")) {
8572 if (tm.tm_hour > 1) {
8573 ast_copy_string(fn, "digits/pt-as", sizeof(fn));
8574 } else {
8575 ast_copy_string(fn, "digits/pt-a", sizeof(fn));
8576 }
8577 if (!res)
8578 res = wait_file(chan, ints, fn, lang);
8579 } else {
8580 ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
8581 if (!res)
8582 res = wait_file(chan, ints, fn, lang);
8583 if (tm.tm_hour != 1)
8584 if (!res)
8585 res = wait_file(chan, ints, "digits/pt-sss", lang);
8586 if (!res)
8587 res = ast_say_time(chan, t, ints, lang);
8588 }
8589 return res;
8590}
8591
8592/*! \brief Hebrew syntax */
8593int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8594{
8595 int res = 0;
8596 struct timeval nowt = ast_tvnow(), when = { t, 0 };
8597 int daydiff;
8598 struct ast_tm tm;
8599 struct ast_tm now;
8600 char fn[256];
8601
8602 ast_localtime(&when, &tm, NULL);
8603 ast_localtime(&nowt, &now, NULL);
8604 daydiff = now.tm_yday - tm.tm_yday;
8605 if ((daydiff < 0) || (daydiff > 6)) {
8606 /* Day of month and month */
8607 if (!res) {
8608 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8609 res = ast_streamfile(chan, fn, lang);
8610 if (!res)
8611 res = ast_waitstream(chan, ints);
8612 }
8613 if (!res) {
8614 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
8615 }
8616 } else if (daydiff) {
8617 /* Just what day of the week */
8618 if (!res) {
8619 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8620 res = ast_streamfile(chan, fn, lang);
8621 if (!res) {
8622 res = ast_waitstream(chan, ints);
8623 }
8624 }
8625 } /* Otherwise, it was today */
8626 if (!res) {
8627 res = ast_say_time(chan, t, ints, lang);
8628 }
8629 return res;
8630}
8631
8632
8633
8634/*! \brief Greek
8635 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
8636 */
8637static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
8638 int tmp;
8639 int left;
8640 int res;
8641 char fn[256] = "";
8642
8643 /* ast_debug(1, "\n\n Saying number female %s %d \n\n", lang, num); */
8644 if (num < 5) {
8645 snprintf(fn, sizeof(fn), "digits/female-%d", num);
8646 res = wait_file(chan, ints, fn, lang);
8647 } else if (num < 13) {
8648 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
8649 } else if (num <100 ) {
8650 tmp = (num/10) * 10;
8651 left = num - tmp;
8652 snprintf(fn, sizeof(fn), "digits/%d", tmp);
8653 res = ast_streamfile(chan, fn, lang);
8654 if (!res)
8655 res = ast_waitstream(chan, ints);
8656 if (left)
8657 gr_say_number_female(left, chan, ints, lang);
8658
8659 } else {
8660 return -1;
8661 }
8662 return res;
8663}
8664
8665
8666
8667/*! \brief Greek support
8668 * A list of the files that you need to create
8669 -> digits/xilia = "xilia"
8670 -> digits/myrio = "ekatomyrio"
8671 -> digits/thousands = "xiliades"
8672 -> digits/millions = "ektatomyria"
8673 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
8674 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
8675 e.g. 80 = "ogdonta"
8676 Here we must note that we use digits/tens/100 to utter "ekato"
8677 and digits/hundred-100 to utter "ekaton"
8678 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
8679 "terakosia". Here again we use hundreds/1000 for "xilia"
8680 and digits/thousands for "xiliades"
8681*/
8682static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8683{
8684 int res = 0;
8685 char fn[256] = "";
8686
8687 if (!num) {
8688 ast_copy_string(fn, "digits/0", sizeof(fn));
8689 res = ast_streamfile(chan, fn, ast_channel_language(chan));
8690 if (!res)
8691 return ast_waitstream(chan, ints);
8692 }
8693
8694 while (!res && num ) {
8695 if (num < 13) {
8696 snprintf(fn, sizeof(fn), "digits/%d", num);
8697 num = 0;
8698 } else if (num <= 100) {
8699 /* 13 < num <= 100 */
8700 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8701 num %= 10;
8702 } else if (num < 200) {
8703 /* 100 < num < 200 */
8704 snprintf(fn, sizeof(fn), "digits/hundred-100");
8705 num %= 100;
8706 } else if (num < 1000) {
8707 /* 200 < num < 1000 */
8708 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
8709 num %= 100;
8710 } else if (num < 2000){
8711 snprintf(fn, sizeof(fn), "digits/xilia");
8712 num %= 1000;
8713 } else {
8714 /* num > 1000 */
8715 if (num < 1000000) {
8716 res = ast_say_number_full_gr(chan, (num / 1000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8717 if (res)
8718 return res;
8719 num %= 1000;
8720 snprintf(fn, sizeof(fn), "digits/thousands");
8721 } else {
8722 if (num < 1000000000) { /* 1,000,000,000 */
8723 res = ast_say_number_full_gr(chan, (num / 1000000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8724 if (res)
8725 return res;
8726 num %= 1000000;
8727 snprintf(fn, sizeof(fn), "digits/millions");
8728 } else {
8729 ast_debug(1, "Number '%d' is too big for me\n", num);
8730 res = -1;
8731 }
8732 }
8733 }
8734 if (!res) {
8735 if (!ast_streamfile(chan, fn, language)) {
8736 if ((audiofd > -1) && (ctrlfd > -1))
8737 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8738 else
8739 res = ast_waitstream(chan, ints);
8740 }
8741 ast_stopstream(chan);
8742 }
8743 }
8744 return res;
8745}
8746
8747/* Japanese syntax */
8748static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8749{
8750 int res = 0;
8751 int playh = 0;
8752 char fn[256] = "";
8753
8754 if (!num)
8755 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
8756
8757 while (!res && (num || playh)) {
8758 if (num < 0) {
8759 ast_copy_string(fn, "digits/minus", sizeof(fn));
8760 if ( num > INT_MIN ) {
8761 num = -num;
8762 } else {
8763 num = 0;
8764 }
8765 } else if (playh) {
8766 ast_copy_string(fn, "digits/hundred", sizeof(fn));
8767 playh = 0;
8768 } else if (num < 20) {
8769 snprintf(fn, sizeof(fn), "digits/%d", num);
8770 num = 0;
8771 } else if (num < 100) {
8772 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8773 num %= 10;
8774 } else {
8775 if (num < 1000){
8776 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
8777 playh++;
8778 num %= 100;
8779 } else {
8780 if (num < 1000000) { /* 1,000,000 */
8781 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
8782 if (res)
8783 return res;
8784 num %= 1000;
8785 snprintf(fn, sizeof(fn), "digits/thousand");
8786 } else {
8787 if (num < 1000000000) { /* 1,000,000,000 */
8788 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
8789 if (res)
8790 return res;
8791 num %= 1000000;
8792 ast_copy_string(fn, "digits/million", sizeof(fn));
8793 } else {
8794 ast_debug(1, "Number '%d' is too big for me\n", num);
8795 res = -1;
8796 }
8797 }
8798 }
8799 }
8800 if (!res) {
8801 if (!ast_streamfile(chan, fn, language)) {
8802 if ((audiofd > -1) && (ctrlfd > -1))
8803 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8804 else
8805 res = ast_waitstream(chan, ints);
8806 }
8807 ast_stopstream(chan);
8808 }
8809 }
8810 return res;
8811}
8812
8813
8814/*! \brief Greek support
8815 *
8816 * The format is weekday - day - month -year
8817 *
8818 * A list of the files that you need to create
8819 * digits/day-[1..7] : "Deytera .. Paraskeyh"
8820 * digits/months/1..12 : "Ianouariou .. Dekembriou"
8821 Attention the months are in "gekinh klhsh"
8822 */
8823static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8824{
8825 struct ast_tm tm;
8826 struct timeval when = { t, 0 };
8827
8828 char fn[256];
8829 int res = 0;
8830
8831
8832 ast_localtime(&when, &tm, NULL);
8833 /* W E E K - D A Y */
8834 if (!res) {
8835 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8836 res = ast_streamfile(chan, fn, lang);
8837 if (!res)
8838 res = ast_waitstream(chan, ints);
8839 }
8840 /* D A Y */
8841 if (!res) {
8842 gr_say_number_female(tm.tm_mday, chan, ints, lang);
8843 }
8844 /* M O N T H */
8845 if (!res) {
8846 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8847 res = ast_streamfile(chan, fn, lang);
8848 if (!res)
8849 res = ast_waitstream(chan, ints);
8850 }
8851 /* Y E A R */
8852 if (!res)
8853 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8854 return res;
8855}
8856
8857
8858/* Japanese syntax */
8859int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8860{
8861 struct ast_tm tm;
8862 struct timeval tv = { t, 0 };
8863 char fn[256];
8864 int res = 0;
8865
8866 ast_localtime(&tv, &tm, NULL);
8867
8868 if (!res)
8869 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8870 if (!res)
8871 res = ast_waitstream(chan, ints);
8872 if (!res)
8873 res = ast_streamfile(chan, "digits/nen", lang);
8874 if (!res) {
8875 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8876 res = ast_streamfile(chan, fn, lang);
8877 if (!res)
8878 res = ast_waitstream(chan, ints);
8879 }
8880 if (!res)
8881 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
8882 if (!res)
8883 res = ast_streamfile(chan, "digits/nichi", lang);
8884 if (!res) {
8885 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8886 res = ast_streamfile(chan, fn, lang);
8887 if (!res)
8888 res = ast_waitstream(chan, ints);
8889 }
8890 return res;
8891}
8892
8893
8894/*! \brief Greek support
8895 *
8896 * A list of the files that you need to create
8897 * - digits/female/1..4 : "Mia, dyo , treis, tesseris "
8898 * - digits/kai : "KAI"
8899 * - digits : "h wra"
8900 * - digits/p-m : "meta meshmbrias"
8901 * - digits/a-m : "pro meshmbrias"
8902 */
8903static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8904{
8905
8906 struct timeval when = { t, 0 };
8907 struct ast_tm tm;
8908 int res = 0;
8909 int hour, pm=0;
8910
8911 ast_localtime(&when, &tm, NULL);
8912 hour = tm.tm_hour;
8913
8914 if (!hour)
8915 hour = 12;
8916 else if (hour == 12)
8917 pm = 1;
8918 else if (hour > 12) {
8919 hour -= 12;
8920 pm = 1;
8921 }
8922
8923 res = gr_say_number_female(hour, chan, ints, lang);
8924 if (tm.tm_min) {
8925 if (!res)
8926 res = ast_streamfile(chan, "digits/kai", lang);
8927 if (!res)
8928 res = ast_waitstream(chan, ints);
8929 if (!res)
8930 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8931 } else {
8932 if (!res)
8933 res = ast_streamfile(chan, "digits/hwra", lang);
8934 if (!res)
8935 res = ast_waitstream(chan, ints);
8936 }
8937 if (pm) {
8938 if (!res)
8939 res = ast_streamfile(chan, "digits/p-m", lang);
8940 } else {
8941 if (!res)
8942 res = ast_streamfile(chan, "digits/a-m", lang);
8943 }
8944 if (!res)
8945 res = ast_waitstream(chan, ints);
8946 return res;
8947}
8948
8949
8950/* Japanese */
8951static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8952{
8953 struct timeval tv = { t, 0 };
8954 struct ast_tm tm;
8955 int res = 0;
8956 int hour, pm=0;
8957
8958 ast_localtime(&tv, &tm, NULL);
8959 hour = tm.tm_hour;
8960
8961 if (!hour)
8962 hour = 12;
8963 else if (hour == 12)
8964 pm = 1;
8965 else if (hour > 12) {
8966 hour -= 12;
8967 pm = 1;
8968 }
8969
8970 if (pm) {
8971 if (!res)
8972 res = ast_streamfile(chan, "digits/p-m", lang);
8973 } else {
8974 if (!res)
8975 res = ast_streamfile(chan, "digits/a-m", lang);
8976 }
8977 if (hour == 9 || hour == 21) {
8978 if (!res)
8979 res = ast_streamfile(chan, "digits/9_2", lang);
8980 } else {
8981 if (!res)
8982 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8983 }
8984 if (!res)
8985 res = ast_streamfile(chan, "digits/ji", lang);
8986 if (!res)
8987 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8988 if (!res)
8989 res = ast_streamfile(chan, "digits/fun", lang);
8990 if (!res)
8991 res = ast_waitstream(chan, ints);
8992 return res;
8993}
8994
8995
8996/*! \brief Greek support
8997 */
8998static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8999{
9000 struct timeval when = { t, 0 };
9001 struct ast_tm tm;
9002 char fn[256];
9003 int res = 0;
9004
9005 ast_localtime(&when, &tm, NULL);
9006
9007 /* W E E K - D A Y */
9008 if (!res) {
9009 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
9010 res = ast_streamfile(chan, fn, lang);
9011 if (!res)
9012 res = ast_waitstream(chan, ints);
9013 }
9014 /* D A Y */
9015 if (!res) {
9016 gr_say_number_female(tm.tm_mday, chan, ints, lang);
9017 }
9018 /* M O N T H */
9019 if (!res) {
9020 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9021 res = ast_streamfile(chan, fn, lang);
9022 if (!res)
9023 res = ast_waitstream(chan, ints);
9024 }
9025
9026 res = ast_say_time_gr(chan, t, ints, lang);
9027 return res;
9028}
9029
9030/* Japanese syntax */
9031int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9032{
9033 struct timeval tv = { t, 0 };
9034 struct ast_tm tm;
9035 char fn[256];
9036 int res = 0;
9037 int hour, pm = 0;
9038
9039 ast_localtime(&tv, &tm, NULL);
9040
9041 if (!res)
9042 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9043 if (!res)
9044 res = ast_streamfile(chan, "digits/nen", lang);
9045 if (!res) {
9046 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9047 res = ast_streamfile(chan, fn, lang);
9048 if (!res)
9049 res = ast_waitstream(chan, ints);
9050 }
9051 if (!res)
9052 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9053 if (!res)
9054 res = ast_streamfile(chan, "digits/nichi", lang);
9055 if (!res) {
9056 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
9057 res = ast_streamfile(chan, fn, lang);
9058 if (!res)
9059 res = ast_waitstream(chan, ints);
9060 }
9061
9062 hour = tm.tm_hour;
9063 if (!hour)
9064 hour = 12;
9065 else if (hour == 12)
9066 pm = 1;
9067 else if (hour > 12) {
9068 hour -= 12;
9069 pm = 1;
9070 }
9071 if (pm) {
9072 if (!res)
9073 res = ast_streamfile(chan, "digits/p-m", lang);
9074 } else {
9075 if (!res)
9076 res = ast_streamfile(chan, "digits/a-m", lang);
9077 }
9078 if (hour == 9 || hour == 21) {
9079 if (!res)
9080 res = ast_streamfile(chan, "digits/9_2", lang);
9081 } else {
9082 if (!res)
9083 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
9084 }
9085 if (!res)
9086 res = ast_streamfile(chan, "digits/ji", lang);
9087 if (!res)
9088 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
9089 if (!res)
9090 res = ast_streamfile(chan, "digits/fun", lang);
9091 if (!res)
9092 res = ast_waitstream(chan, ints);
9093 return res;
9094}
9095
9096/*! \brief Greek support
9097 */
9098static 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)
9099{
9100 struct timeval when = { t, 0 };
9101 struct ast_tm tm;
9102 int res=0, offset, sndoffset;
9103 char sndfile[256], nextmsg[256];
9104
9105 if (!format)
9106 format = "AdBY 'digits/at' IMp";
9107
9108 ast_localtime(&when, &tm, tzone);
9109
9110 for (offset=0 ; format[offset] != '\0' ; offset++) {
9111 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
9112 switch (format[offset]) {
9113 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
9114 case '\'':
9115 /* Literal name of a sound file */
9116 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
9117 sndfile[sndoffset] = format[offset];
9118 }
9119 sndfile[sndoffset] = '\0';
9120 res = wait_file(chan, ints, sndfile, lang);
9121 break;
9122 case 'A':
9123 case 'a':
9124 /* Sunday - Saturday */
9125 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
9126 res = wait_file(chan, ints, nextmsg, lang);
9127 break;
9128 case 'B':
9129 case 'b':
9130 case 'h':
9131 /* January - December */
9132 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
9133 res = wait_file(chan, ints, nextmsg, lang);
9134 break;
9135 case 'd':
9136 case 'e':
9137 /* first - thirtyfirst */
9138 gr_say_number_female(tm.tm_mday, chan, ints, lang);
9139 break;
9140 case 'Y':
9141 /* Year */
9142
9143 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, ast_channel_language(chan), -1, -1);
9144 break;
9145 case 'I':
9146 case 'l':
9147 /* 12-Hour */
9148 if (tm.tm_hour == 0)
9149 gr_say_number_female(12, chan, ints, lang);
9150 else if (tm.tm_hour > 12)
9151 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
9152 else
9153 gr_say_number_female(tm.tm_hour, chan, ints, lang);
9154 break;
9155 case 'H':
9156 case 'k':
9157 /* 24-Hour */
9158 gr_say_number_female(tm.tm_hour, chan, ints, lang);
9159 break;
9160 case 'M':
9161 /* Minute */
9162 if (tm.tm_min) {
9163 if (!res)
9164 res = ast_streamfile(chan, "digits/kai", lang);
9165 if (!res)
9166 res = ast_waitstream(chan, ints);
9167 if (!res)
9168 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
9169 } else {
9170 if (!res)
9171 res = ast_streamfile(chan, "digits/oclock", lang);
9172 if (!res)
9173 res = ast_waitstream(chan, ints);
9174 }
9175 break;
9176 case 'P':
9177 case 'p':
9178 /* AM/PM */
9179 if (tm.tm_hour > 11)
9180 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
9181 else
9182 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
9183 res = wait_file(chan, ints, nextmsg, lang);
9184 break;
9185 case 'Q':
9186 /* Shorthand for "Today", "Yesterday", or ABdY */
9187 /* XXX As emphasized elsewhere, this should the native way in your
9188 * language to say the date, with changes in what you say, depending
9189 * upon how recent the date is. XXX */
9190 {
9191 struct timeval now = ast_tvnow();
9192 struct ast_tm tmnow;
9193 time_t beg_today;
9194
9195 ast_localtime(&now, &tmnow, tzone);
9196 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9197 /* In any case, it saves not having to do ast_mktime() */
9198 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9199 if (beg_today < t) {
9200 /* Today */
9201 res = wait_file(chan, ints, "digits/today", lang);
9202 } else if (beg_today - 86400 < t) {
9203 /* Yesterday */
9204 res = wait_file(chan, ints, "digits/yesterday", lang);
9205 } else {
9206 res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
9207 }
9208 }
9209 break;
9210 case 'q':
9211 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9212 /* XXX As emphasized elsewhere, this should the native way in your
9213 * language to say the date, with changes in what you say, depending
9214 * upon how recent the date is. XXX */
9215 {
9216 struct timeval now = ast_tvnow();
9217 struct ast_tm tmnow;
9218 time_t beg_today;
9219
9220 ast_localtime(&now, &tmnow, tzone);
9221 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9222 /* In any case, it saves not having to do ast_mktime() */
9223 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9224 if (beg_today < t) {
9225 /* Today */
9226 } else if ((beg_today - 86400) < t) {
9227 /* Yesterday */
9228 res = wait_file(chan, ints, "digits/yesterday", lang);
9229 } else if (beg_today - 86400 * 6 < t) {
9230 /* Within the last week */
9231 res = ast_say_date_with_format_gr(chan, t, ints, lang, "A", tzone);
9232 } else {
9233 res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
9234 }
9235 }
9236 break;
9237 case 'R':
9238 res = ast_say_date_with_format_gr(chan, t, ints, lang, "HM", tzone);
9239 break;
9240 case 'S':
9241 /* Seconds */
9242 ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
9243 res = wait_file(chan, ints, nextmsg, lang);
9244 if (!res)
9245 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
9246 if (!res)
9247 ast_copy_string(nextmsg, "seconds", sizeof(nextmsg));
9248 res = wait_file(chan, ints, nextmsg, lang);
9249 break;
9250 case 'T':
9251 res = ast_say_date_with_format_gr(chan, t, ints, lang, "HMS", tzone);
9252 break;
9253 case ' ':
9254 case ' ':
9255 /* Just ignore spaces and tabs */
9256 break;
9257 default:
9258 /* Unknown character */
9259 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9260 }
9261 /* Jump out on DTMF */
9262 if (res) {
9263 break;
9264 }
9265 }
9266 return res;
9267}
9268
9269/* Japanese syntax */
9270int ast_say_date_with_format_ja(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
9271{
9272 struct timeval tv = { time, 0 };
9273 struct ast_tm tm;
9274 int res = 0, offset, sndoffset;
9275 char sndfile[256], nextmsg[256];
9276
9277 if (!format)
9278 format = "YbdAPIMS";
9279
9280 ast_localtime(&tv, &tm, timezone);
9281
9282 for (offset = 0; format[offset] != '\0'; offset++) {
9283 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
9284 switch (format[offset]) {
9285 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
9286 case '\'':
9287 /* Literal name of a sound file */
9288 for (sndoffset = 0; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
9289 sndfile[sndoffset] = format[offset];
9290 }
9291 sndfile[sndoffset] = '\0';
9292 res = wait_file(chan,ints,sndfile,lang);
9293 break;
9294 case 'A':
9295 case 'a':
9296 /* Sunday - Saturday */
9297 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
9298 res = wait_file(chan,ints,nextmsg,lang);
9299 break;
9300 case 'B':
9301 case 'b':
9302 case 'h':
9303 /* January - December */
9304 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
9305 res = wait_file(chan,ints,nextmsg,lang);
9306 break;
9307 case 'd':
9308 case 'e':
9309 /* First - Thirtyfirst */
9310 if (tm.tm_mday < 21) {
9311 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d_2", tm.tm_mday);
9312 res = wait_file(chan,ints,nextmsg,lang);
9313 } else if (tm.tm_mday < 30) {
9314 /* Between 21 and 29 - two sounds */
9315 res = wait_file(chan,ints, "digits/20",lang);
9316 if (!res) {
9317 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
9318 res = wait_file(chan,ints,nextmsg,lang);
9319 }
9320 res = wait_file(chan,ints, "digits/nichi",lang);
9321 } else if (tm.tm_mday == 30) {
9322 /* 30 */
9323 res = wait_file(chan,ints, "digits/h-30_2",lang);
9324 } else {
9325 /* 31 */
9326 res = wait_file(chan,ints, "digits/30",lang);
9327 res = wait_file(chan,ints, "digits/1",lang);
9328 res = wait_file(chan,ints, "digits/nichi",lang);
9329 }
9330 break;
9331 case 'Y':
9332 /* Year */
9333 if (tm.tm_year > 99) {
9334 res = wait_file(chan,ints, "digits/2",lang);
9335 if (!res) {
9336 res = wait_file(chan,ints, "digits/thousand",lang);
9337 }
9338 if (tm.tm_year > 100) {
9339 if (!res) {
9340 /* This works until the end of 2020 */
9341 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
9342 res = wait_file(chan,ints,nextmsg,lang);
9343 }
9344 }
9345 } else {
9346 if (tm.tm_year < 1) {
9347 /* I'm not going to handle 1900 and prior */
9348 /* We'll just be silent on the year, instead of bombing out. */
9349 } else {
9350 res = wait_file(chan,ints, "digits/19",lang);
9351 if (!res) {
9352 if (tm.tm_year <= 9) {
9353 /* 1901 - 1909 */
9354 res = wait_file(chan,ints, "digits/oh",lang);
9355 if (!res) {
9356 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
9357 res = wait_file(chan,ints,nextmsg,lang);
9358 }
9359 } else if (tm.tm_year <= 20) {
9360 /* 1910 - 1920 */
9361 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
9362 res = wait_file(chan,ints,nextmsg,lang);
9363 } else {
9364 /* 1921 - 1999 */
9365 int ten, one;
9366 ten = tm.tm_year / 10;
9367 one = tm.tm_year % 10;
9368 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
9369 res = wait_file(chan,ints,nextmsg,lang);
9370 if (!res) {
9371 if (one != 0) {
9372 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
9373 res = wait_file(chan,ints,nextmsg,lang);
9374 }
9375 }
9376 }
9377 }
9378 }
9379 }
9380 res = wait_file(chan,ints, "digits/nen",lang);
9381 break;
9382 case 'P':
9383 case 'p':
9384 /* AM/PM */
9385 if (tm.tm_hour > 11)
9386 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
9387 else
9388 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
9389 res = wait_file(chan,ints,nextmsg,lang);
9390 break;
9391 case 'I':
9392 case 'l':
9393 /* 12-Hour */
9394 if (tm.tm_hour == 0)
9395 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
9396 else if (tm.tm_hour == 9 || tm.tm_hour == 21)
9397 snprintf(nextmsg,sizeof(nextmsg), "digits/9_2");
9398 else if (tm.tm_hour > 12)
9399 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
9400 else
9401 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
9402 res = wait_file(chan,ints,nextmsg,lang);
9403 if(!res) res = wait_file(chan,ints, "digits/ji",lang);
9404 break;
9405 case 'H':
9406 case 'k':
9407 if (!res) {
9408 if (tm.tm_hour != 0) {
9409 int remainder = tm.tm_hour;
9410 if (tm.tm_hour > 20) {
9411 res = wait_file(chan,ints, "digits/20",lang);
9412 remainder -= 20;
9413 }
9414 if (!res) {
9415 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
9416 res = wait_file(chan,ints,nextmsg,lang);
9417 }
9418 }
9419 }
9420 res = wait_file(chan,ints, "digits/ji",lang);
9421 break;
9422 case 'M':
9423 /* Minute */
9424 if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
9425 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
9426 res = wait_file(chan,ints,nextmsg,lang);
9427 } else {
9428 int ten, one;
9429 ten = (tm.tm_min / 10) * 10;
9430 one = (tm.tm_min % 10);
9431 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
9432 res = wait_file(chan,ints,nextmsg,lang);
9433 if (!res) {
9434 /* Fifty, not fifty-zero */
9435 if (one != 0) {
9436 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
9437 res = wait_file(chan,ints,nextmsg,lang);
9438 }
9439 }
9440 }
9441 res = wait_file(chan,ints, "digits/fun",lang);
9442 break;
9443 case 'Q':
9444 /* Shorthand for "Today", "Yesterday", or ABdY */
9445 {
9446 struct timeval now;
9447 struct ast_tm tmnow;
9448 time_t beg_today;
9449
9450 gettimeofday(&now,NULL);
9451 ast_localtime(&now,&tmnow,timezone);
9452 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9453 /* In any case, it saves not having to do ast_mktime() */
9454 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9455 if (beg_today < time) {
9456 /* Today */
9457 res = wait_file(chan,ints, "digits/today",lang);
9458 } else if (beg_today - 86400 < time) {
9459 /* Yesterday */
9460 res = wait_file(chan,ints, "digits/yesterday",lang);
9461 } else {
9462 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
9463 }
9464 }
9465 break;
9466 case 'q':
9467 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9468 {
9469 struct timeval now;
9470 struct ast_tm tmnow;
9471 time_t beg_today;
9472
9473 gettimeofday(&now,NULL);
9474 ast_localtime(&now,&tmnow,timezone);
9475 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9476 /* In any case, it saves not having to do ast_mktime() */
9477 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9478 if (beg_today < time) {
9479 /* Today */
9480 } else if ((beg_today - 86400) < time) {
9481 /* Yesterday */
9482 res = wait_file(chan,ints, "digits/yesterday",lang);
9483 } else if (beg_today - 86400 * 6 < time) {
9484 /* Within the last week */
9485 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
9486 } else {
9487 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
9488 }
9489 }
9490 break;
9491 case 'R':
9492 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
9493 break;
9494 case 'S':
9495 /* Seconds */
9496 if (tm.tm_sec == 0) {
9497 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
9498 res = wait_file(chan,ints,nextmsg,lang);
9499 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
9500 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
9501 res = wait_file(chan,ints,nextmsg,lang);
9502 } else {
9503 int ten, one;
9504 ten = (tm.tm_sec / 10) * 10;
9505 one = (tm.tm_sec % 10);
9506 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
9507 res = wait_file(chan,ints,nextmsg,lang);
9508 if (!res) {
9509 /* Fifty, not fifty-zero */
9510 if (one != 0) {
9511 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
9512 res = wait_file(chan,ints,nextmsg,lang);
9513 }
9514 }
9515 }
9516 res = wait_file(chan,ints, "digits/byou",lang);
9517 break;
9518 case 'T':
9519 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
9520 break;
9521 case ' ':
9522 case ' ':
9523 /* Just ignore spaces and tabs */
9524 break;
9525 default:
9526 /* Unknown character */
9527 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9528 }
9529 /* Jump out on DTMF */
9530 if (res) {
9531 break;
9532 }
9533 }
9534 return res;
9535}
9536
9537/*! \brief Vietnamese syntax */
9538int 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)
9539{
9540 struct timeval when = { t, 0 };
9541 struct ast_tm tm;
9542 int res = 0, offset, sndoffset;
9543 char sndfile[256], nextmsg[256];
9544
9545 if (format == NULL)
9546 format = "A 'digits/day' eB 'digits/year' Y 'digits/at' k 'hours' M 'minutes' p";
9547
9548 ast_localtime(&when, &tm, tzone);
9549
9550 for (offset=0 ; format[offset] != '\0' ; offset++) {
9551 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
9552 switch (format[offset]) {
9553 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
9554 case '\'':
9555 /* Literal name of a sound file */
9556 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
9557 sndfile[sndoffset] = format[offset];
9558 }
9559 sndfile[sndoffset] = '\0';
9560 res = wait_file(chan, ints, sndfile, lang);
9561 break;
9562 case 'A':
9563 case 'a':
9564 /* Sunday - Saturday */
9565 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
9566 res = wait_file(chan, ints, nextmsg, lang);
9567 break;
9568 case 'B':
9569 case 'b':
9570 case 'h':
9571 /* January - December */
9572 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
9573 res = wait_file(chan, ints, nextmsg, lang);
9574 break;
9575 case 'm':
9576 /* Month enumerated */
9577 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
9578 break;
9579 case 'd':
9580 case 'e':
9581 /* 1 - 31 */
9582 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9583 break;
9584 case 'Y':
9585 /* Year */
9586 if (tm.tm_year > 99) {
9587 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9588 } else if (tm.tm_year < 1) {
9589 /* I'm not going to handle 1900 and prior */
9590 /* We'll just be silent on the year, instead of bombing out. */
9591 } else {
9592 res = wait_file(chan, ints, "digits/19", lang);
9593 if (!res) {
9594 if (tm.tm_year <= 9) {
9595 /* 1901 - 1909 */
9596 res = wait_file(chan, ints, "digits/odd", lang);
9597 }
9598
9599 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
9600 }
9601 }
9602 break;
9603 case 'I':
9604 case 'l':
9605 /* 12-Hour */
9606 if (tm.tm_hour == 0)
9607 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
9608 else if (tm.tm_hour > 12)
9609 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
9610 else
9611 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
9612 res = wait_file(chan, ints, nextmsg, lang);
9613 break;
9614 case 'H':
9615 case 'k':
9616 /* 24-Hour */
9617 if (format[offset] == 'H') {
9618 /* e.g. oh-eight */
9619 if (tm.tm_hour < 10) {
9620 res = wait_file(chan, ints, "digits/0", lang);
9621 }
9622 } else {
9623 /* e.g. eight */
9624 if (tm.tm_hour == 0) {
9625 res = wait_file(chan, ints, "digits/0", lang);
9626 }
9627 }
9628 if (!res) {
9629 if (tm.tm_hour != 0) {
9630 int remaining = tm.tm_hour;
9631 if (tm.tm_hour > 20) {
9632 res = wait_file(chan, ints, "digits/20", lang);
9633 remaining -= 20;
9634 }
9635 if (!res) {
9636 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
9637 res = wait_file(chan, ints, nextmsg, lang);
9638 }
9639 }
9640 }
9641 break;
9642 case 'M':
9643 case 'N':
9644 /* Minute */
9645 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
9646 break;
9647 case 'P':
9648 case 'p':
9649 /* AM/PM */
9650 if (tm.tm_hour > 11)
9651 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
9652 else
9653 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
9654 res = wait_file(chan, ints, nextmsg, lang);
9655 break;
9656 case 'Q':
9657 /* Shorthand for "Today", "Yesterday", or ABdY */
9658 /* XXX As emphasized elsewhere, this should the native way in your
9659 * language to say the date, with changes in what you say, depending
9660 * upon how recent the date is. XXX */
9661 {
9662 struct timeval now = ast_tvnow();
9663 struct ast_tm tmnow;
9664 time_t beg_today;
9665
9666 gettimeofday(&now, NULL);
9667 ast_localtime(&now, &tmnow, tzone);
9668 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9669 /* In any case, it saves not having to do ast_mktime() */
9670 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9671 if (beg_today < t) {
9672 /* Today */
9673 res = wait_file(chan, ints, "digits/today", lang);
9674 } else if (beg_today - 86400 < t) {
9675 /* Yesterday */
9676 res = wait_file(chan, ints, "digits/yesterday", lang);
9677 } else if (beg_today - 86400 * 6 < t) {
9678 /* Within the last week */
9679 res = ast_say_date_with_format_vi(chan, t, ints, lang, "A", tzone);
9680 } else if (beg_today - 2628000 < t) {
9681 /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9682 res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9683 } else if (beg_today - 15768000 < t) {
9684 /* Less than 6 months ago - "August seventh" */
9685 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9686 } else {
9687 /* More than 6 months ago - "April nineteenth two thousand three" */
9688 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9689 }
9690 }
9691 break;
9692 case 'q':
9693 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9694 /* XXX As emphasized elsewhere, this should the native way in your
9695 * language to say the date, with changes in what you say, depending
9696 * upon how recent the date is. XXX */
9697 {
9698 struct timeval now;
9699 struct ast_tm tmnow;
9700 time_t beg_today;
9701
9702 now = ast_tvnow();
9703 ast_localtime(&now, &tmnow, tzone);
9704 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9705 /* In any case, it saves not having to do ast_mktime() */
9706 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9707 if (beg_today < t) {
9708 /* Today */
9709 } else if ((beg_today - 86400) < t) {
9710 /* Yesterday */
9711 res = wait_file(chan, ints, "digits/yesterday", lang);
9712 } else if (beg_today - 86400 * 6 < t) {
9713 /* Within the last week */
9714 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
9715 } else if (beg_today - 2628000 < t) {
9716 /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9717 res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9718 } else if (beg_today - 15768000 < t) {
9719 /* Less than 6 months ago - "August seventh" */
9720 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9721 } else {
9722 /* More than 6 months ago - "April nineteenth two thousand three" */
9723 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9724 }
9725 }
9726 break;
9727 case 'R':
9728 res = ast_say_date_with_format_vi(chan, t, ints, lang, "HM", tzone);
9729 break;
9730 case 'S':
9731 /* Seconds */
9732 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
9733 break;
9734 case 'T':
9735 res = ast_say_date_with_format_vi(chan, t, ints, lang, "H 'hours' M 'minutes' S 'seconds'", tzone);
9736 break;
9737 case ' ':
9738 case ' ':
9739 /* Just ignore spaces and tabs */
9740 break;
9741 default:
9742 /* Unknown character */
9743 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9744 }
9745 /* Jump out on DTMF */
9746 if (res) {
9747 break;
9748 }
9749 }
9750 return res;
9751}
9752
9753/*! \brief Georgian support
9754
9755 Convert a number into a semi-localized string. Only for Georgian.
9756 res must be of at least 256 bytes, preallocated.
9757 The output corresponds to Georgian spoken numbers, so
9758 it may be either converted to real words by applying a direct conversion
9759 table, or played just by substituting the entities with played files.
9760
9761 Output may consist of the following tokens (separated by spaces):
9762 0, minus.
9763 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
9764 10-19.
9765 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
9766 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
9767 1000, 1000_. (atasi, atas).
9768 1000000, 1000000_. (milioni, milion).
9769 1000000000, 1000000000_. (miliardi, miliard).
9770
9771 To be able to play the sounds, each of the above tokens needs
9772 a corresponding sound file. (e.g. 200_.gsm).
9773*/
9774static char* ast_translate_number_ka(int num, char* res, int res_len)
9775{
9776 char buf[256];
9777 int digit = 0;
9778 int remaining = 0;
9779
9780
9781 if (num < 0) {
9782 strncat(res, "minus ", res_len - strlen(res) - 1);
9783 if ( num > INT_MIN ) {
9784 num = -num;
9785 } else {
9786 num = 0;
9787 }
9788 }
9789
9790
9791 /* directly read the numbers */
9792 if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
9793 snprintf(buf, sizeof(buf), "%d", num);
9794 strncat(res, buf, res_len - strlen(res) - 1);
9795 return res;
9796 }
9797
9798
9799 if (num < 40) { /* ocda... */
9800 strncat(res, "20_ ", res_len - strlen(res) - 1);
9801 return ast_translate_number_ka(num - 20, res, res_len);
9802 }
9803
9804 if (num < 60) { /* ormocda... */
9805 strncat(res, "40_ ", res_len - strlen(res) - 1);
9806 return ast_translate_number_ka(num - 40, res, res_len);
9807 }
9808
9809 if (num < 80) { /* samocda... */
9810 strncat(res, "60_ ", res_len - strlen(res) - 1);
9811 return ast_translate_number_ka(num - 60, res, res_len);
9812 }
9813
9814 if (num < 100) { /* otxmocda... */
9815 strncat(res, "80_ ", res_len - strlen(res) - 1);
9816 return ast_translate_number_ka(num - 80, res, res_len);
9817 }
9818
9819
9820 if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
9821 remaining = num % 100;
9822 digit = (num - remaining) / 100;
9823
9824 if (remaining == 0) {
9825 snprintf(buf, sizeof(buf), "%d", num);
9826 strncat(res, buf, res_len - strlen(res) - 1);
9827 return res;
9828 } else {
9829 snprintf(buf, sizeof(buf), "%d_ ", digit*100);
9830 strncat(res, buf, res_len - strlen(res) - 1);
9831 return ast_translate_number_ka(remaining, res, res_len);
9832 }
9833 }
9834
9835
9836 if (num == 1000) {
9837 strncat(res, "1000", res_len - strlen(res) - 1);
9838 return res;
9839 }
9840
9841
9842 if (num < 1000000) {
9843 remaining = num % 1000;
9844 digit = (num - remaining) / 1000;
9845
9846 if (remaining == 0) {
9847 ast_translate_number_ka(digit, res, res_len);
9848 strncat(res, " 1000", res_len - strlen(res) - 1);
9849 return res;
9850 }
9851
9852 if (digit == 1) {
9853 strncat(res, "1000_ ", res_len - strlen(res) - 1);
9854 return ast_translate_number_ka(remaining, res, res_len);
9855 }
9856
9857 ast_translate_number_ka(digit, res, res_len);
9858 strncat(res, " 1000_ ", res_len - strlen(res) - 1);
9859 return ast_translate_number_ka(remaining, res, res_len);
9860 }
9861
9862
9863 if (num == 1000000) {
9864 strncat(res, "1 1000000", res_len - strlen(res) - 1);
9865 return res;
9866 }
9867
9868
9869 if (num < 1000000000) {
9870 remaining = num % 1000000;
9871 digit = (num - remaining) / 1000000;
9872
9873 if (remaining == 0) {
9874 ast_translate_number_ka(digit, res, res_len);
9875 strncat(res, " 1000000", res_len - strlen(res) - 1);
9876 return res;
9877 }
9878
9879 ast_translate_number_ka(digit, res, res_len);
9880 strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
9881 return ast_translate_number_ka(remaining, res, res_len);
9882 }
9883
9884
9885 if (num == 1000000000) {
9886 strncat(res, "1 1000000000", res_len - strlen(res) - 1);
9887 return res;
9888 }
9889
9890
9891 if (num > 1000000000) {
9892 remaining = num % 1000000000;
9893 digit = (num - remaining) / 1000000000;
9894
9895 if (remaining == 0) {
9896 ast_translate_number_ka(digit, res, res_len);
9897 strncat(res, " 1000000000", res_len - strlen(res) - 1);
9898 return res;
9899 }
9900
9901 ast_translate_number_ka(digit, res, res_len);
9902 strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
9903 return ast_translate_number_ka(remaining, res, res_len);
9904 }
9905
9906 return res;
9907
9908}
9909
9910
9911
9912/*! \brief ast_say_number_full_ka: Georgian syntax */
9913static 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)
9914{
9915 int res = 0;
9916 char fn[512] = "";
9917 const char* s = 0;
9918 const char* remaining = fn;
9919
9920 if (!num) {
9921 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
9922 }
9923
9924
9925 ast_translate_number_ka(num, fn, 512);
9926
9927
9928
9929 while (res == 0 && (s = strstr(remaining, " "))) {
9930 size_t len = s - remaining;
9931 char* new_string = ast_malloc(len + 1 + strlen("digits/"));
9932
9933 sprintf(new_string, "digits/");
9934 strncat(new_string, remaining, len); /* we can't sprintf() it, it's not null-terminated. */
9935/* new_string[len + strlen("digits/")] = '\0'; */
9936
9937 if (!ast_streamfile(chan, new_string, language)) {
9938 if ((audiofd > -1) && (ctrlfd > -1)) {
9939 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9940 } else {
9941 res = ast_waitstream(chan, ints);
9942 }
9943 }
9944 ast_stopstream(chan);
9945
9946 ast_free(new_string);
9947
9948 remaining = s + 1; /* position just after the found space char. */
9949 while (*remaining == ' ') { /* skip multiple spaces */
9950 remaining++;
9951 }
9952 }
9953
9954
9955 /* the last chunk. */
9956 if (res == 0 && *remaining) {
9957
9958 char* new_string = ast_malloc(strlen(remaining) + 1 + strlen("digits/"));
9959 sprintf(new_string, "digits/%s", remaining);
9960
9961 if (!ast_streamfile(chan, new_string, language)) {
9962 if ((audiofd > -1) && (ctrlfd > -1)) {
9963 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9964 } else {
9965 res = ast_waitstream(chan, ints);
9966 }
9967 }
9968 ast_stopstream(chan);
9969
9970 ast_free(new_string);
9971
9972 }
9973
9974
9975 return res;
9976
9977}
9978
9979
9980
9981/*! \brief Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
9982
9983Georgian support for date/time requires the following files (*.gsm):
9984
9985 - mon-1, mon-2, ... (ianvari, tebervali, ...)
9986 - day-1, day-2, ... (orshabati, samshabati, ...)
9987 - saati_da
9988 - tsuti
9989 - tslis
9990*/
9991static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9992{
9993 struct timeval when = { t, 0 };
9994 struct ast_tm tm;
9995 char fn[256];
9996 int res = 0;
9997 ast_localtime(&when, &tm, NULL);
9998
9999 if (!res) {
10000 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
10001 }
10002
10003 if (!res) {
10004 snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
10005 res = ast_streamfile(chan, fn, lang);
10006 if (!res) {
10007 res = ast_waitstream(chan, ints);
10008 }
10009 }
10010
10011 if (!res) {
10012 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
10013/* if (!res)
10014 res = ast_waitstream(chan, ints);
10015*/
10016 }
10017
10018 if (!res) {
10019 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
10020 res = ast_streamfile(chan, fn, lang);
10021 if (!res) {
10022 res = ast_waitstream(chan, ints);
10023 }
10024 }
10025 return res;
10026
10027}
10028
10029
10030
10031
10032
10033/*! \brief Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
10034static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
10035{
10036 struct timeval when = { t, 0 };
10037 struct ast_tm tm;
10038 int res = 0;
10039
10040 ast_localtime(&when, &tm, NULL);
10041
10042 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
10043 if (!res) {
10044 res = ast_streamfile(chan, "digits/saati_da", lang);
10045 if (!res) {
10046 res = ast_waitstream(chan, ints);
10047 }
10048 }
10049
10050 if (tm.tm_min) {
10051 if (!res) {
10052 res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
10053
10054 if (!res) {
10055 res = ast_streamfile(chan, "digits/tsuti", lang);
10056 if (!res) {
10057 res = ast_waitstream(chan, ints);
10058 }
10059 }
10060 }
10061 }
10062 return res;
10063}
10064
10065
10066
10067/*! \brief Georgian syntax. Say date, then say time. */
10068static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
10069{
10070 struct timeval when = { t, 0 };
10071 struct ast_tm tm;
10072 int res = 0;
10073
10074 ast_localtime(&when, &tm, NULL);
10075 res = ast_say_date(chan, t, ints, lang);
10076 if (!res) {
10077 ast_say_time(chan, t, ints, lang);
10078 }
10079 return res;
10080
10081}
10082
10083
10084
10085
10086/*! \brief Georgian syntax */
10087static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
10088{
10089 int res=0;
10090 int daydiff;
10091 struct ast_tm tm;
10092 struct ast_tm now;
10093 struct timeval when = { t, 0 }, nowt = ast_tvnow();
10094 char fn[256];
10095
10096 ast_localtime(&when, &tm, NULL);
10097 ast_localtime(&nowt, &now, NULL);
10098 daydiff = now.tm_yday - tm.tm_yday;
10099 if ((daydiff < 0) || (daydiff > 6)) {
10100 /* Day of month and month */
10101 if (!res) {
10102 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
10103 }
10104 if (!res) {
10105 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
10106 res = ast_streamfile(chan, fn, lang);
10107 if (!res) {
10108 res = ast_waitstream(chan, ints);
10109 }
10110 }
10111
10112 } else if (daydiff) {
10113 /* Just what day of the week */
10114 if (!res) {
10115 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
10116 res = ast_streamfile(chan, fn, lang);
10117 if (!res) {
10118 res = ast_waitstream(chan, ints);
10119 }
10120 }
10121 } /* Otherwise, it was today */
10122 if (!res) {
10123 res = ast_say_time(chan, t, ints, lang);
10124 }
10125
10126 return res;
10127}
10128
10129/*! \brief
10130 * In English, we use the plural for everything but one. For example:
10131 * - 1 degree
10132 * - 2 degrees
10133 * - 5 degrees
10134 * The filename for the plural form is generated by appending "s". Note that
10135 * purpose is to generate a unique filename, not to implement irregular
10136 * declensions. Thus:
10137 * - 1 man
10138 * - 2 mans (the "mans" soundfile will of course say "men")
10139 */
10140static const char *counted_noun_ending_en(int num)
10141{
10142 if (num == 1 || num == -1) {
10143 return "";
10144 } else {
10145 return "s";
10146 }
10147}
10148
10149/*! \brief
10150 * Counting of objects in slavic languages such as Russian and Ukrainian the
10151 * rules are more complicated. There are two plural forms used in counting.
10152 * They are the genative singular which we represent with the suffix "x1" and
10153 * the genative plural which we represent with the suffix "x2". The base names
10154 * of the soundfiles remain in English. For example:
10155 * - 1 degree (soundfile says "gradus")
10156 * - 2 degreex1 (soundfile says "gradusa")
10157 * - 5 degreex2 (soundfile says "gradusov")
10158 */
10159static const char *counted_noun_ending_slavic(int num)
10160{
10161 if (num < 0) {
10162 num *= -1;
10163 }
10164 num %= 100; /* never pay attention to more than two digits */
10165 if (num >= 20) { /* for numbers 20 and above, pay attention to only last digit */
10166 num %= 10;
10167 }
10168 if (num == 1) { /* singular */
10169 return "";
10170 }
10171 if (num > 0 && num < 5) { /* 2--4 get genative singular */
10172 return "x1";
10173 } else { /* 5--19 get genative plural */
10174 return "x2";
10175 }
10176}
10177
10178int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
10179{
10180 char *temp;
10181 int temp_len;
10182 const char *ending;
10183 if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
10184 ending = counted_noun_ending_slavic(num);
10185 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
10186 ending = counted_noun_ending_slavic(num);
10187 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
10188 ending = counted_noun_ending_slavic(num);
10189 } else { /* English and default */
10190 ending = counted_noun_ending_en(num);
10191 }
10192 temp = ast_alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
10193 snprintf(temp, temp_len, "%s%s", noun, ending);
10194 return ast_play_and_wait(chan, temp);
10195}
10196
10197/*! \brief
10198 * In slavic languages such as Russian and Ukrainian the rules for declining
10199 * adjectives are simpler than those for nouns. When counting we use only
10200 * the singular (to which we give no suffix) and the genative plural (which
10201 * we represent by adding an "x"). Oh, an in the singular gender matters
10202 * so we append the supplied gender suffix ("m", "f", "n").
10203 */
10204static const char *counted_adjective_ending_ru(int num, const char gender[])
10205{
10206 if (num < 0) {
10207 num *= -1;
10208 }
10209 num %= 100; /* never pay attention to more than two digits */
10210 if (num >= 20) { /* at 20 and beyond only the last digit matters */
10211 num %= 10;
10212 }
10213 if (num == 1) {
10214 return gender ? gender : "";
10215 } else { /* all other numbers get the genative plural */
10216 return "x";
10217 }
10218}
10219
10220int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
10221{
10222 char *temp;
10223 int temp_len;
10224 const char *ending;
10225 if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
10226 ending = counted_adjective_ending_ru(num, gender);
10227 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
10228 ending = counted_adjective_ending_ru(num, gender);
10229 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
10230 ending = counted_adjective_ending_ru(num, gender);
10231 } else { /* English and default */
10232 ending = "";
10233 }
10234 temp = ast_alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
10235 snprintf(temp, temp_len, "%s%s", adjective, ending);
10236 return ast_play_and_wait(chan, temp);
10237}
10238
10239/*! \brief
10240 * remap the 'say' functions to use those in this file
10241 */
char digit
const char * str
Definition app_jack.c:150
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
#define ast_log
Definition astobj2.c:42
static char language[MAX_LANGUAGE]
Definition chan_iax2.c:361
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
const char * ast_channel_language(const struct ast_channel *chan)
Standard Command Line Interface.
#define ESS(x)
Definition cli.h:59
char buf[BUFSIZE]
Definition eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int monfd)
Definition file.c:1953
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition file.c:233
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition file.c:1382
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition file.c:1218
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition file.c:1944
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
Play a stream and wait for a digit, returning the digit that was pressed.
Definition main/app.c:1617
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
Custom localtime functions for multiple timezones.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition localtime.c:1739
Asterisk locking-related definitions:
#define NULL
Definition resample.c:96
static struct ast_str * get_ordinal_str_en(int num, const char *lang)
Definition say.c:578
static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition say.c:4442
static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
French syntax oclock = heure.
Definition say.c:6270
static int ast_say_number_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_en_dr: Dari syntax New files:
Definition say.c:1563
#define IL_DATE_STR
static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_enumeration_full_en: English syntax
Definition say.c:3499
static int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Spanish syntax.
Definition say.c:6079
static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang)
Greek digits/female-[1..4] : "Mia, dyo , treis, tessereis".
Definition say.c:8637
static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_vi: Vietnamese syntax
Definition say.c:3377
static struct ast_str * get_number_str_en(int num, const char *lang)
Definition say.c:467
static int exp10_int(int power)
Definition say.c:966
static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_it: Italian
Definition say.c:2213
static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:8448
static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Brazilian Portuguese syntax.
Definition say.c:8266
static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:7678
static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Dutch syntax.
Definition say.c:6698
static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition say.c:8180
static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition say.c:7912
static 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)
ast_say_enumeration_full_is: Icelandic syntax
Definition say.c:4228
static int get_lastdigits_ru(int num)
determine last digits for thousands/millions (ru)
Definition say.c:3201
static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition say.c:8543
static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition say.c:7766
static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax.
Definition say.c:10087
static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition say.c:8199
static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
Definition say.c:349
static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
Definition say.c:2849
static char * pl_append(char *buffer, char *str)
Definition say.c:2571
static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Definition say.c:8748
static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax. Say date, then say time.
Definition say.c:10068
static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
Definition say.c:295
static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Definition say.c:4805
#define IL_DATE_STR_FULL
static void __say_init(void)
remap the 'say' functions to use those in this file
Definition say.c:10242
static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
Definition say.c:3142
struct ast_str * ast_get_ordinal_str(int num, const char *lang)
ast_get_ordinal_str: call language-specific functions
Definition say.c:687
static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_nl: dutch syntax New files: digits/nl-en
Definition say.c:2368
static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition say.c:8593
static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_cs: Czech syntax
Definition say.c:996
static int ast_say_date_with_format_vi(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Vietnamese syntax.
Definition say.c:9538
static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition say.c:7972
static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
Definition say.c:4139
static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_ru: Russian syntax
Definition say.c:3228
static char * pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
Definition say.c:2558
static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:9031
static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_de: German syntax
Definition say.c:1208
static 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)
Definition say.c:9270
static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Polish syntax.
Definition say.c:6901
static const char * counted_adjective_ending_ru(int num, const char gender[])
In slavic languages such as Russian and Ukrainian the rules for declining adjectives are simpler than...
Definition say.c:10204
static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition say.c:8823
static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition say.c:4696
static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Definition say.c:3794
static 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)
ast_say_number_full_is: Icelandic syntax
Definition say.c:2093
static int ast_say_number_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_ps: Pashto syntax
Definition say.c:1346
static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition say.c:4602
static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
Definition say.c:865
static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Danish syntax.
Definition say.c:4471
static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition say.c:4520
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
Definition say.c:10220
static int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Thai syntax.
Definition say.c:5747
struct ast_str * ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity)
Returns an ast_str of files for SayAlpha playback.
Definition say.c:65
static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition say.c:8998
#define SAY_NUM_BUF_SIZE
Definition say.c:1840
static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_es: Spanish syntax
Definition say.c:1645
static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition say.c:4631
static int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Danish syntax.
Definition say.c:5124
static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition say.c:8383
static int ast_say_number_full_ka(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_ka: Georgian syntax
Definition say.c:9913
static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition say.c:7833
struct ast_str * ast_get_number_str(int num, const char *lang)
ast_get_number_str: call language-specific functions
Definition say.c:567
static 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)
Definition say.c:5535
#define IL_TIME_STR
static 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)
Definition say.c:211
static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition say.c:8043
static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_en_GB: British syntax New files:
Definition say.c:1487
static int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
German syntax.
Definition say.c:5326
static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
Definition say.c:1841
static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
say_ordinal_full
Definition say.c:960
struct ast_str * ast_get_phonetic_str(const char *str, const char *lang)
Returns an ast_str of files for SayPhonetic playback.
Definition say.c:217
static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_hu: Hungarian syntax
Definition say.c:2013
static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_no: Norwegian syntax New files: In addition to American English,...
Definition say.c:2466
static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition say.c:8903
static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition say.c:7786
static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:4757
static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_fr: French syntax Extra sounds needed: 1F: feminin 'une' et: 'and'
Definition say.c:1749
static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition say.c:8505
static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition say.c:7717
static int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Italian syntax.
Definition say.c:6466
static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_zh: Taiwanese / Chinese syntax
Definition say.c:3029
static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Greek support.
Definition say.c:9098
static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:8951
static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition say.c:8125
static int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
ast_say_date_with_format_he Say formatted date in Hebrew
Definition say.c:5932
struct ast_str * ast_get_money_str(const char *str, const char *lang)
ast_get_money_str: call language-specific functions
Definition say.c:450
static const char * find_first_and(const char *lang)
Definition say.c:877
static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Thai syntax.
Definition say.c:3307
static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition say.c:8139
static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Taiwanese / Chinese syntax.
Definition say.c:7930
static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition say.c:7809
static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition say.c:4570
static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Greek support A list of the files that you need to create -> digits/xilia = "xilia" -> digits/myrio =...
Definition say.c:8682
static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:8008
static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_se: Swedish syntax
Definition say.c:2949
static int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Taiwanese / Chinese syntax.
Definition say.c:7410
static struct ast_str * ast_get_money_en_dollars_str(const char *str, const char *lang)
Definition say.c:355
static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full_da: Danish syntax New files:
Definition say.c:1094
static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:8859
static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition say.c:8110
static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition say.c:7853
static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_en: English syntax
Definition say.c:953
static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition say.c:4723
static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
Definition say.c:2578
static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Taiwanese / Chinese syntax.
Definition say.c:8323
static const char * counted_noun_ending_slavic(int num)
Counting of objects in slavic languages such as Russian and Ukrainian the rules are more complicated....
Definition say.c:10159
static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax. e.g. "otxi saati da eqvsi tsuti".
Definition say.c:10034
static char next_item(const char *format)
Definition say.c:5117
struct ast_str * ast_get_digit_str(const char *str, const char *lang)
Returns an ast_str of files for SayDigits playback.
Definition say.c:301
static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition say.c:4660
static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
Definition say.c:461
int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
Definition say.c:10178
static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition say.c:8467
static char * ast_translate_number_ka(int num, char *res, int res_len)
Georgian support.
Definition say.c:9774
static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition say.c:4403
static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full: call language-specific functions
Definition say.c:890
static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
Definition say.c:2753
static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_enumeration_full_da: Danish syntax
Definition say.c:3814
static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
Definition say.c:2592
static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Brazilian Portuguese syntax.
Definition say.c:7882
static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)
Definition say.c:176
static const char * counted_noun_ending_en(int num)
In English, we use the plural for everything but one. For example:
Definition say.c:10140
static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
Portuguese syntax.
Definition say.c:7118
static int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
English syntax.
Definition say.c:4846
static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition say.c:8280
static int ast_say_enumeration_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_enumeration_full_ps: Pashto syntax
Definition say.c:3598
static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_enumeration_full: call language-specific functions
Definition say.c:3473
static int ast_say_enumeration_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_enumeration_full_dr: Dari syntax
Definition say.c:3697
static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_enumeration_full_de: German syntax
Definition say.c:3977
static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
Definition say.c:9991
Say numbers and dates (maybe words one day too)
SAY_EXTERN int(* ast_say_money_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_money_str_full)
Definition say.h:170
SAY_EXTERN int(* ast_say_datetime_from_now)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_datetime_from_now)
Definition say.h:206
SAY_EXTERN int(* ast_say_date)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_date)
Definition say.h:204
SAY_EXTERN int(* ast_say_number_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_number_full)
Same as ast_say_number() with audiofd for received audio and returns 1 on ctrlfd being readable.
Definition say.h:86
SAY_EXTERN int(* ast_say_enumeration_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_enumeration_full)
Same as ast_say_enumeration() with audiofd for received audio and returns 1 on ctrlfd being readable.
Definition say.h:125
SAY_EXTERN int(* ast_say_ordinal_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_ordinal_full)
Same as ast_say_number() with audiofd for received audio and returns 1 on ctrlfd being readable.
Definition say.h:105
SAY_EXTERN int(* ast_say_character_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd) SAY_INIT(ast_say_character_str_full)
Definition say.h:194
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition channel.c:8411
SAY_EXTERN int(* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format)
Definition say.h:208
SAY_EXTERN int(* ast_say_digit_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_digit_str_full)
Same as ast_say_digit_str() with audiofd for received audio and returns 1 on ctrlfd being readable.
Definition say.h:162
int ast_say_enumeration(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says an enumeration
Definition channel.c:8423
ast_say_case_sensitivity
Controls how ast_say_character_str denotes the case of characters in a string.
Definition say.h:181
@ AST_SAY_CASE_LOWER
Definition say.h:183
@ AST_SAY_CASE_ALL
Definition say.h:185
@ AST_SAY_CASE_UPPER
Definition say.h:184
@ AST_SAY_CASE_NONE
Definition say.h:182
int ast_say_digits_full(struct ast_channel *chan, int num, const char *ints, const char *lang, int audiofd, int ctrlfd)
Same as ast_say_digits() with audiofd for received audio and returns 1 on ctrlfd being readable.
Definition channel.c:8459
SAY_EXTERN int(* ast_say_time)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_time)
Definition say.h:202
SAY_EXTERN int(* ast_say_phonetic_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_phonetic_str_full)
Definition say.h:199
SAY_EXTERN int(* ast_say_datetime)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_datetime)
Definition say.h:201
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
char *attribute_pure ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition strings.h:161
Main Channel structure associated with a channel.
Support for dynamic strings.
Definition strings.h:623
int tm_mday
Definition localtime.h:39
int tm_sec
Definition localtime.h:36
int tm_wday
Definition localtime.h:42
int tm_hour
Definition localtime.h:38
int tm_yday
Definition localtime.h:43
int tm_min
Definition localtime.h:37
int tm_year
Definition localtime.h:41
int tm_mon
Definition localtime.h:40
char * separator_dziesiatek
Definition say.c:2549
char * cyfry2[10]
Definition say.c:2551
char * cyfry[10]
Definition say.c:2550
char * rzedy[3][3]
Definition say.c:2555
char * nastki[10]
Definition say.c:2554
char * setki[10]
Definition say.c:2552
char * dziesiatki[10]
Definition say.c:2553
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition test.h:189
static struct test_options options
static struct test_val b
Time-related functions and macros.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
Utility functions.