Asterisk - The Open Source Telephony Project GIT-master-a358458
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
60/* Forward declaration */
61static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
62
63struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
64 const char *fn;
65 char fnbuf[10], asciibuf[20] = "letters/ascii";
66 char ltr;
67 int num = 0;
68 int res = 0;
69 int upper = 0;
70 int lower = 0;
71
72 struct ast_str *filenames = ast_str_create(20);
73 if (!filenames) {
74 return NULL;
75 }
76 ast_str_reset(filenames);
77
78 while (str[num] && !res) {
79 fn = NULL;
80 switch (str[num]) {
81 case ('*'):
82 fn = "digits/star";
83 break;
84 case ('#'):
85 fn = "digits/pound";
86 break;
87 case ('!'):
88 fn = "letters/exclaimation-point";
89 break;
90 case ('@'):
91 fn = "letters/at";
92 break;
93 case ('$'):
94 fn = "letters/dollar";
95 break;
96 case ('-'):
97 fn = "letters/dash";
98 break;
99 case ('.'):
100 fn = "letters/dot";
101 break;
102 case ('='):
103 fn = "letters/equals";
104 break;
105 case ('+'):
106 fn = "letters/plus";
107 break;
108 case ('/'):
109 fn = "letters/slash";
110 break;
111 case (' '):
112 fn = "letters/space";
113 break;
114 case ('0'):
115 case ('1'):
116 case ('2'):
117 case ('3'):
118 case ('4'):
119 case ('5'):
120 case ('6'):
121 case ('7'):
122 case ('8'):
123 case ('9'):
124 strcpy(fnbuf, "digits/X");
125 fnbuf[7] = str[num];
126 fn = fnbuf;
127 break;
128 default:
129 ltr = str[num];
130 if ('A' <= ltr && ltr <= 'Z') {
131 ltr += 'a' - 'A'; /* file names are all lower-case */
132 switch (sensitivity) {
134 case AST_SAY_CASE_ALL:
135 upper = !upper;
138 break;
139 }
140 } else if ('a' <= ltr && ltr <= 'z') {
141 switch (sensitivity) {
143 case AST_SAY_CASE_ALL:
144 lower = !lower;
147 break;
148 }
149 }
150
151 if (upper) {
152 strcpy(fnbuf, "uppercase");
153 } else if (lower) {
154 strcpy(fnbuf, "lowercase");
155 } else {
156 strcpy(fnbuf, "letters/X");
157 fnbuf[8] = ltr;
158 }
159 fn = fnbuf;
160 }
161 if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
162 (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
163 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
164 }
165 if (upper || lower) {
166 continue;
167 }
168 num++;
169 }
170
171 return filenames;
172}
173
174static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)
175{
176 int res = 0;
177 char *files;
178 const char *fn;
179
180 if (!filenames) {
181 return -1;
182 }
183
184 /* No filenames to play? Return success so we don't hang up erroneously */
185 if (ast_str_strlen(filenames) == 0) {
186 ast_free(filenames);
187 return 0;
188 }
189
190 files = ast_str_buffer(filenames);
191
192 while (!res && (fn = strsep(&files, "&"))) {
193 res = ast_streamfile(chan, fn, lang);
194 if (!res) {
195 if ((audiofd > -1) && (ctrlfd > -1)) {
196 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
197 } else {
198 res = ast_waitstream(chan, ints);
199 }
200 }
201 ast_stopstream(chan);
202 }
203
204 ast_free(filenames);
205
206 return res;
207}
208
209static 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)
210{
211 struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
212 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
213}
214
215struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
216{
217 const char *fn;
218 char fnbuf[256];
219 char ltr;
220 int num = 0;
221
222 struct ast_str *filenames = ast_str_create(20);
223 if (!filenames) {
224 return NULL;
225 }
226 ast_str_reset(filenames);
227
228 while (str[num]) {
229 fn = NULL;
230 switch (str[num]) {
231 case ('*'):
232 fn = "digits/star";
233 break;
234 case ('#'):
235 fn = "digits/pound";
236 break;
237 case ('!'):
238 fn = "letters/exclaimation-point";
239 break;
240 case ('@'):
241 fn = "letters/at";
242 break;
243 case ('$'):
244 fn = "letters/dollar";
245 break;
246 case ('-'):
247 fn = "letters/dash";
248 break;
249 case ('.'):
250 fn = "letters/dot";
251 break;
252 case ('='):
253 fn = "letters/equals";
254 break;
255 case ('+'):
256 fn = "letters/plus";
257 break;
258 case ('/'):
259 fn = "letters/slash";
260 break;
261 case (' '):
262 fn = "letters/space";
263 break;
264 case ('0'):
265 case ('1'):
266 case ('2'):
267 case ('3'):
268 case ('4'):
269 case ('5'):
270 case ('6'):
271 case ('7'):
272 case ('8'):
273 strcpy(fnbuf, "digits/X");
274 fnbuf[7] = str[num];
275 fn = fnbuf;
276 break;
277 default: /* '9' falls here... */
278 ltr = str[num];
279 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
280 strcpy(fnbuf, "phonetic/X_p");
281 fnbuf[9] = ltr;
282 fn = fnbuf;
283 }
284 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
285 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
286 }
287 num++;
288 }
289
290 return filenames;
291}
292
293static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
294{
295 struct ast_str *filenames = ast_get_phonetic_str(str, lang);
296 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
297}
298
299struct ast_str* ast_get_digit_str(const char *str, const char *lang)
300{
301 const char *fn;
302 char fnbuf[256];
303 int num = 0;
304
305 struct ast_str *filenames = ast_str_create(20);
306 if (!filenames) {
307 return NULL;
308 }
309 ast_str_reset(filenames);
310
311 while (str[num]) {
312 fn = NULL;
313 switch (str[num]) {
314 case ('*'):
315 fn = "digits/star";
316 break;
317 case ('#'):
318 fn = "digits/pound";
319 break;
320 case ('-'):
321 fn = "digits/minus";
322 break;
323 case '0':
324 case '1':
325 case '2':
326 case '3':
327 case '4':
328 case '5':
329 case '6':
330 case '7':
331 case '8':
332 case '9':
333 strcpy(fnbuf, "digits/X");
334 fnbuf[7] = str[num];
335 fn = fnbuf;
336 break;
337 }
338 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
339 ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
340 }
341 num++;
342 }
343
344 return filenames;
345}
346
347static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
348{
349 struct ast_str *filenames = ast_get_digit_str(str, lang);
350 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
351}
352
353static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
354{
355 const char *fnr;
356
357 double dollars = 0;
358 int amt, cents;
359 struct ast_str *fnrecurse = NULL;
360
361 struct ast_str *filenames = ast_str_create(20);
362 if (!filenames) {
363 return NULL;
364 }
365 ast_str_reset(filenames);
366
367 if (sscanf(str, "%30lf", &dollars) != 1) {
368 amt = 0;
369 } else { /* convert everything to cents */
370 amt = dollars * 100;
371 }
372
373 /* Just the cents after the dollar decimal point */
374 cents = amt - (((int) dollars) * 100);
375 ast_debug(1, "Cents is %d, amount is %d\n", cents, amt);
376
377 if (amt >= 100) {
378 fnrecurse = ast_get_number_str((amt / 100), lang);
379 if (!fnrecurse) {
380 ast_log(LOG_WARNING, "Couldn't get string for dollars\n");
381 } else {
382 fnr = ast_str_buffer(fnrecurse);
383 ast_str_append(&filenames, 0, "%s", fnr);
384 }
385
386 /* If this is it, end on a down pitch, otherwise up pitch */
387 if (amt < 200) {
388 ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
389 } else {
390 ast_str_append(&filenames, 0, "&%s", "dollars");
391 }
392
393 /* If dollars and cents, add "and" in the middle */
394 if (cents > 0) {
395 ast_str_append(&filenames, 0, "&%s", "and");
396 }
397 }
398
399 if (cents > 0) {
400 fnrecurse = ast_get_number_str(cents, lang);
401 if (!fnrecurse) {
402 ast_log(LOG_ERROR, "Couldn't get string for cents\n");
403 } else {
404 fnr = ast_str_buffer(fnrecurse);
405 ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
406 }
407 ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
408 } else if (amt == 0) {
409 fnrecurse = ast_get_digit_str("0", lang);
410 if (!fnrecurse) {
411 ast_log(LOG_ERROR, "Couldn't get string for cents\n");
412 } else {
413 fnr = ast_str_buffer(fnrecurse);
414 ast_str_append(&filenames, 0, "%s", fnr);
415 }
416 ast_str_append(&filenames, 0, "&%s", "cents");
417 }
418
419 if (fnrecurse) {
420 ast_free(fnrecurse);
421 }
422
423 return filenames;
424}
425
426/*! \brief ast_get_money_str: call language-specific functions */
427struct ast_str* ast_get_money_str(const char *str, const char *lang)
428{
429 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
430 return ast_get_money_en_dollars_str(str, lang);
431 }
432
433 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
434 /* Default to english */
435 return ast_get_money_en_dollars_str(str, lang);
436}
437
438static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
439{
440 struct ast_str *filenames = ast_get_money_str(str, lang);
441 return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
442}
443
444static struct ast_str* get_number_str_en(int num, const char *lang)
445{
446 const char *fnr;
447 int loops = 0;
448
449 int res = 0;
450 int playh = 0;
451 char fn[256] = "";
452
453 struct ast_str *filenames;
454
455 if (!num) {
456 return ast_get_digit_str("0", lang);
457 }
458
459 filenames = ast_str_create(20);
460 if (!filenames) {
461 return NULL;
462 }
463 ast_str_reset(filenames);
464
465 while (!res && (num || playh)) {
466 if (num < 0) {
467 ast_copy_string(fn, "digits/minus", sizeof(fn));
468 if ( num > INT_MIN ) {
469 num = -num;
470 } else {
471 num = 0;
472 }
473 } else if (playh) {
474 ast_copy_string(fn, "digits/hundred", sizeof(fn));
475 playh = 0;
476 } else if (num < 20) {
477 snprintf(fn, sizeof(fn), "digits/%d", num);
478 num = 0;
479 } else if (num < 100) {
480 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
481 num %= 10;
482 } else {
483 if (num < 1000){
484 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
485 playh++;
486 num %= 100;
487 } else {
488 struct ast_str *fnrecurse = NULL;
489 if (num < 1000000) { /* 1,000,000 */
490 fnrecurse = get_number_str_en((num / 1000), lang);
491 if (!fnrecurse) {
492 ast_log(LOG_ERROR, "Couldn't get string for num\n");
493 } else {
494 fnr = ast_str_buffer(fnrecurse);
495 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
496 }
497 num %= 1000;
498 snprintf(fn, sizeof(fn), "&digits/thousand");
499 } else {
500 if (num < 1000000000) { /* 1,000,000,000 */
501 fnrecurse = get_number_str_en((num / 1000000), lang);
502 if (!fnrecurse) {
503 ast_log(LOG_ERROR, "Couldn't get string for num\n");
504 } else {
505 fnr = ast_str_buffer(fnrecurse);
506 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
507 }
508 num %= 1000000;
509 ast_copy_string(fn, "&digits/million", sizeof(fn));
510 } else {
511 if (num < INT_MAX) {
512 fnrecurse = get_number_str_en((num / 1000000000), lang);
513 if (!fnrecurse) {
514 ast_log(LOG_ERROR, "Couldn't get string for num\n");
515 } else {
516 fnr = ast_str_buffer(fnrecurse);
517 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
518 }
519 num %= 1000000000;
520 ast_copy_string(fn, "&digits/billion", sizeof(fn));
521 } else {
522 ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
523 res = -1;
524 }
525 }
526 }
527 if (fnrecurse) {
528 ast_free(fnrecurse);
529 }
530 /* we already decided whether or not to add an &, don't add another one immediately */
531 loops = 0;
532 }
533 }
534 if (!res) {
535 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
536 loops++;
537 }
538 }
539
540 return filenames;
541}
542
543/*! \brief ast_get_number_str: call language-specific functions */
544struct ast_str* ast_get_number_str(int num, const char *lang)
545{
546 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
547 return get_number_str_en(num, lang);
548 }
549
550 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
551 /* Default to english */
552 return get_number_str_en(num, lang);
553}
554
555static struct ast_str* get_ordinal_str_en(int num, const char *lang)
556{
557 const char *fnr;
558 int loops = 0;
559
560 int res = 0;
561 int playh = 0;
562 char fn[256] = "";
563
564 struct ast_str *filenames;
565
566 if (!num) {
567 num = 0;
568 }
569
570 filenames = ast_str_create(20);
571 if (!filenames) {
572 return NULL;
573 }
574 ast_str_reset(filenames);
575
576 while (!res && (num || playh)) {
577 if (num < 0) {
578 ast_copy_string(fn, "digits/minus", sizeof(fn));
579 if ( num > INT_MIN ) {
580 num = -num;
581 } else {
582 num = 0;
583 }
584 } else if (playh) {
585 ast_copy_string(fn, (num % 100 == 0) ? "digits/h-hundred" : "digits/hundred", sizeof(fn));
586 playh = 0;
587 } else if (num < 20) {
588 if (num > 0) {
589 snprintf(fn, sizeof(fn), "digits/h-%d", num);
590 } else {
591 ast_log(LOG_ERROR, "Unsupported ordinal number: %d\n", num);
592 }
593 num = 0;
594 } else if (num < 100) {
595 int base = (num / 10) * 10;
596 if (base != num) {
597 snprintf(fn, sizeof(fn), "digits/%d", base);
598 } else {
599 snprintf(fn, sizeof(fn), "digits/h-%d", base);
600 }
601 num %= 10;
602 } else {
603 if (num < 1000){
604 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
605 playh++;
606 num %= 100;
607 } else {
608 struct ast_str *fnrecurse = NULL;
609 if (num < 1000000) { /* 1,000,000 */
610 fnrecurse = get_number_str_en((num / 1000), lang);
611 if (!fnrecurse) {
612 ast_log(LOG_ERROR, "Couldn't get string for num\n");
613 } else {
614 fnr = ast_str_buffer(fnrecurse);
615 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
616 }
617 num %= 1000;
618 snprintf(fn, sizeof(fn), (num % 1000 == 0) ? "&digits/h-thousand" : "&digits/thousand");
619 } else {
620 if (num < 1000000000) { /* 1,000,000,000 */
621 fnrecurse = get_number_str_en((num / 1000000), lang);
622 if (!fnrecurse) {
623 ast_log(LOG_ERROR, "Couldn't get string for num\n");
624 } else {
625 fnr = ast_str_buffer(fnrecurse);
626 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
627 }
628 num %= 1000000;
629 ast_copy_string(fn, (num % 1000000 == 0) ? "&digits/h-million" : "&digits/million", sizeof(fn));
630 } else {
631 if (num < INT_MAX) {
632 fnrecurse = get_number_str_en((num / 1000000000), lang);
633 if (!fnrecurse) {
634 ast_log(LOG_ERROR, "Couldn't get string for num\n");
635 } else {
636 fnr = ast_str_buffer(fnrecurse);
637 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
638 }
639 num %= 1000000000;
640 ast_copy_string(fn, (num % 1000000000 == 0) ? "&digits/h-billion" : "&digits/billion", sizeof(fn));
641 } else {
642 ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
643 res = -1;
644 }
645 }
646 }
647 if (fnrecurse) {
648 ast_free(fnrecurse);
649 }
650 /* we already decided whether or not to add an &, don't add another one immediately */
651 loops = 0;
652 }
653 }
654 if (!res) {
655 ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
656 loops++;
657 }
658 }
659
660 return filenames;
661}
662
663/*! \brief ast_get_ordinal_str: call language-specific functions */
664struct ast_str* ast_get_ordinal_str(int num, const char *lang)
665{
666 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
667 return get_ordinal_str_en(num, lang);
668 }
669
670 ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
671 /* Default to english */
672 return get_ordinal_str_en(num, lang);
673}
674
675/* Forward declarations */
676/*! \page Def_syntaxlang Asterisk Language Syntaxes supported
677 \note Not really language codes.
678 For these language codes, Asterisk will change the syntax when
679 saying numbers (and in some cases dates and voicemail messages
680 as well)
681 \arg \b da - Danish
682 \arg \b de - German
683 \arg \b en - English (US)
684 \arg \b en_GB - English (British)
685 \arg \b es - Spanish, Mexican
686 \arg \b fr - French
687 \arg \b he - Hebrew
688 \arg \b is - Icelandic
689 \arg \b it - Italian
690 \arg \b nl - Dutch
691 \arg \b no - Norwegian
692 \arg \b pl - Polish
693 \arg \b pt - Portuguese
694 \arg \b pt_BR - Portuguese (Brazil)
695 \arg \b se - Swedish
696 \arg \b zh - Taiwanese / Chinese
697 \arg \b ru - Russian
698 \arg \b ka - Georgian
699 \arg \b hu - Hungarian
700
701 \par Gender:
702 For some languages the numbers differ for gender of the countable object.
703 Commonly for "one", like "un"/"une" in French. Note that the interface
704 is somewhat peculiar, as differing languages can have conflicting
705 genders.
706 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
707 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
708
709 Date/Time functions currently have less languages supported than saynumber().
710
711 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
712
713 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
714
715 \par Portuguese
716 Portuguese sound files needed for Time/Date functions:
717 pt-ah
718 pt-ao
719 pt-de
720 pt-e
721 pt-ora
722 pt-meianoite
723 pt-meiodia
724 pt-sss
725
726 \par Spanish
727 Spanish sound files needed for Time/Date functions:
728 es-de
729 es-el
730
731 \par Italian
732 Italian sound files needed for Time/Date functions:
733 ore-una
734 ore-mezzanotte
735
736*/
737
738/* Forward declarations of language specific variants of ast_say_number_full */
739static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
740static 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);
741static 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);
742static 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);
743static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
744static 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);
745static 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);
746static 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);
747static 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);
748static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
749static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
750static 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);
751static 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);
752static 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);
753static 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);
754static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
755static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
756static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
757static 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);
758static 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);
759static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
760static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
761static 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);
762static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
763
764/* Forward declarations of language specific variants of ast_say_enumeration_full */
765static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
766static 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);
767static 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);
768static 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);
769static 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);
770static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
771
772/* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
773static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
774static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
775static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
776static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
777static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
778static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
779static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
780static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
781static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
782static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
783static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
784static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
785static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
786
787static 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);
788static 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);
789static 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);
790static 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);
791static 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);
792static 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);
793static 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);
794static 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);
795static 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);
796static 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);
797static 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);
798static 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);
799static 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);
800static 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);
801static 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);
802static 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);
803
804static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
805static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
806static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
807static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
808static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
809static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
810static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
811static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
812static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
813static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
814static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
815static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
816static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
817
818static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
819static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
820static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
821static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
822static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
823static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
824static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
825static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
826static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
827static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
828static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
829static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
830static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
831
832static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
833static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
834static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
835static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
836static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
837
838static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
839{
840 int res;
841 if ((res = ast_streamfile(chan, file, lang))) {
842 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
843 }
844 if (!res) {
845 res = ast_waitstream(chan, ints);
846 }
847 return res;
848}
849
850/*! \brief ast_say_number_full: call language-specific functions
851 \note Called from AGI */
852static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
853{
854 ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
855 if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
856 return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
857 } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
858 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
859 } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
860 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
861 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
862 return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
863 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
864 return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
865 } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
866 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
867 } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
868 return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
869 } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
870 return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
871 } else if (!strncasecmp(language, "ja", 2)) { /* Japanese syntax */
872 return ast_say_number_full_ja(chan, num, ints, language, audiofd, ctrlfd);
873 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
874 return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
875 } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
876 return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
877 } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
878 return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
879 } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
880 return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
881 } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
882 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
883 } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
884 return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
885 } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
886 return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
887 } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
888 return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
889 } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
890 return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
891 } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
892 return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
893 } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
894 return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
895 } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
896 return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
897 } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
898 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
899 } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
900 return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
901 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
902 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
903 }
904
905 /* Default to english */
906 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
907}
908
909/*! \brief ast_say_number_full_en: English syntax
910 \note This is the default syntax, if no other syntax defined in this file is used */
911static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
912{
913 struct ast_str *filenames = ast_get_number_str(num, language);
914 return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
915}
916
917/*! \brief say_ordinal_full */
918static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
919{
920 struct ast_str *filenames = ast_get_ordinal_str(num, language);
921 return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
922}
923
924static int exp10_int(int power)
925{
926 int x, res= 1;
927 for (x=0;x<power;x++)
928 res *= 10;
929 return res;
930}
931
932/*! \brief ast_say_number_full_cs: Czech syntax
933 *
934 * files needed:
935 * - 1m,2m - gender male
936 * - 1w,2w - gender female
937 * - 3,4,...,20
938 * - 30,40,...,90
939 *
940 * - hundreds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
941 *
942 * for each number 10^(3n + 3) exist 3 files represented as:
943 * 1 thousand = jeden tisic = 1_E3
944 * 2,3,4 thousands = dva,tri,ctyri tisice = 2-3_E3
945 * 5,6,... thousands = pet,sest,... tisic = 5_E3
946 *
947 * million = _E6
948 * miliard = _E9
949 * etc...
950 *
951 * thousand, milion are gender male, so 1 and 2 is 1m 2m
952 * miliard is gender female, so 1 and 2 is 1w 2w
953 */
954static 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)
955{
956 int res = 0;
957 int playh = 0;
958 char fn[256] = "";
959
960 int hundred = 0;
961 int left = 0;
962 int length = 0;
963
964 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
965 if (!options)
966 options = "w";
967
968 if (!num)
969 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
970
971 while (!res && (num || playh)) {
972 if (num < 0) {
973 ast_copy_string(fn, "digits/minus", sizeof(fn));
974 if ( num > INT_MIN ) {
975 num = -num;
976 } else {
977 num = 0;
978 }
979 } else if (num < 3 ) {
980 snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
981 playh = 0;
982 num = 0;
983 } else if (num < 20) {
984 snprintf(fn, sizeof(fn), "digits/%d", num);
985 playh = 0;
986 num = 0;
987 } else if (num < 100) {
988 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
989 num %= 10;
990 } else if (num < 1000) {
991 hundred = num / 100;
992 if ( hundred == 1 ) {
993 ast_copy_string(fn, "digits/1sto", sizeof(fn));
994 } else if ( hundred == 2 ) {
995 ast_copy_string(fn, "digits/2ste", sizeof(fn));
996 } else {
997 res = ast_say_number_full_cs(chan, hundred, ints, language, options, audiofd, ctrlfd);
998 if (res)
999 return res;
1000 if (hundred == 3 || hundred == 4) {
1001 ast_copy_string(fn, "digits/sta", sizeof(fn));
1002 } else if ( hundred > 4 ) {
1003 ast_copy_string(fn, "digits/set", sizeof(fn));
1004 }
1005 }
1006 num -= (hundred * 100);
1007 } else { /* num > 1000 */
1008 length = (int)log10(num)+1;
1009 while ( (length % 3 ) != 1 ) {
1010 length--;
1011 }
1012 left = num / (exp10_int(length-1));
1013 if ( left == 2 ) {
1014 switch (length-1) {
1015 case 9: options = "w"; /* 1,000,000,000 gender female */
1016 break;
1017 default : options = "m"; /* others are male */
1018 }
1019 }
1020 if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
1021 res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
1022 if (res)
1023 return res;
1024 }
1025 if ( left >= 5 ) { /* >= 5 have the same declension */
1026 snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
1027 } else if ( left >= 2 && left <= 4 ) {
1028 snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
1029 } else { /* left == 1 */
1030 snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
1031 }
1032 num -= left * (exp10_int(length-1));
1033 }
1034 if (!res) {
1035 if (!ast_streamfile(chan, fn, language)) {
1036 if ((audiofd > -1) && (ctrlfd > -1)) {
1037 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1038 } else {
1039 res = ast_waitstream(chan, ints);
1040 }
1041 }
1042 ast_stopstream(chan);
1043 }
1044 }
1045 return res;
1046}
1047
1048/*! \brief ast_say_number_full_da: Danish syntax
1049 New files:
1050 - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
1051 */
1052static 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)
1053{
1054 int res = 0;
1055 int playh = 0;
1056 int playa = 0;
1057 int cn = 1; /* +1 = commune; -1 = neuter */
1058 char fn[256] = "";
1059 if (!num)
1060 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1061
1062 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1063
1064 while (!res && (num || playh || playa )) {
1065 /* The grammar for Danish numbers is the same as for English except
1066 * for the following:
1067 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
1068 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1069 * "one-and twenty" and 68 is "eight-and sixty".
1070 * - "million" is different in singular and plural form
1071 * - numbers > 1000 with zero as the third digit from last have an
1072 * "and" before the last two digits, i.e. 2034 is "two thousand and
1073 * four-and thirty" and 1000012 is "one million and twelve".
1074 */
1075 if (num < 0) {
1076 ast_copy_string(fn, "digits/minus", sizeof(fn));
1077 if ( num > INT_MIN ) {
1078 num = -num;
1079 } else {
1080 num = 0;
1081 }
1082 } else if (playh) {
1083 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1084 playh = 0;
1085 } else if (playa) {
1086 ast_copy_string(fn, "digits/and", sizeof(fn));
1087 playa = 0;
1088 } else if (num == 1 && cn == -1) {
1089 ast_copy_string(fn, "digits/1N", sizeof(fn));
1090 num = 0;
1091 } else if (num < 20) {
1092 snprintf(fn, sizeof(fn), "digits/%d", num);
1093 num = 0;
1094 } else if (num < 100) {
1095 int ones = num % 10;
1096 if (ones) {
1097 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1098 num -= ones;
1099 } else {
1100 snprintf(fn, sizeof(fn), "digits/%d", num);
1101 num = 0;
1102 }
1103 } else {
1104 if (num < 1000) {
1105 int hundreds = num / 100;
1106 if (hundreds == 1)
1107 ast_copy_string(fn, "digits/1N", sizeof(fn));
1108 else
1109 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1110
1111 playh++;
1112 num -= 100 * hundreds;
1113 if (num)
1114 playa++;
1115
1116 } else {
1117 if (num < 1000000) {
1118 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1119 if (res)
1120 return res;
1121 num = num % 1000;
1122 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1123 } else {
1124 if (num < 1000000000) {
1125 int millions = num / 1000000;
1126 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
1127 if (res)
1128 return res;
1129 if (millions == 1)
1130 ast_copy_string(fn, "digits/million", sizeof(fn));
1131 else
1132 ast_copy_string(fn, "digits/millions", sizeof(fn));
1133 num = num % 1000000;
1134 } else {
1135 ast_debug(1, "Number '%d' is too big for me\n", num);
1136 res = -1;
1137 }
1138 }
1139 if (num && num < 100)
1140 playa++;
1141 }
1142 }
1143 if (!res) {
1144 if (!ast_streamfile(chan, fn, language)) {
1145 if ((audiofd > -1) && (ctrlfd > -1))
1146 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1147 else
1148 res = ast_waitstream(chan, ints);
1149 }
1150 ast_stopstream(chan);
1151 }
1152 }
1153 return res;
1154}
1155
1156/*! \brief ast_say_number_full_de: German syntax
1157
1158 New files:
1159 In addition to English, the following sounds are required:
1160 - "millions"
1161 - "1-and" through "9-and"
1162 - "1F" (eine)
1163 - "1N" (ein)
1164 - NB "1" is recorded as 'eins'
1165 */
1166static 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)
1167{
1168 int res = 0, t = 0;
1169 int mf = 1; /* +1 = male and neuter; -1 = female */
1170 char fn[256] = "";
1171 char fna[256] = "";
1172 if (!num)
1173 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1174
1175 if (options && (!strncasecmp(options, "f", 1)))
1176 mf = -1;
1177
1178 while (!res && num) {
1179 /* The grammar for German numbers is the same as for English except
1180 * for the following:
1181 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1182 * "one-and twenty" and 68 is "eight-and sixty".
1183 * - "one" varies according to gender
1184 * - 100 is 'hundert', however all other instances are 'ein hundert'
1185 * - 1000 is 'tausend', however all other instances are 'ein tausend'
1186 * - 1000000 is always 'eine million'
1187 * - "million" is different in singular and plural form
1188 * - 'and' should not go between a hundreds place value and any
1189 * tens/ones place values that follows it. i.e 136 is ein hundert
1190 * sechs und dreizig, not ein hundert und sechs und dreizig.
1191 */
1192 if (num < 0) {
1193 ast_copy_string(fn, "digits/minus", sizeof(fn));
1194 if ( num > INT_MIN ) {
1195 num = -num;
1196 } else {
1197 num = 0;
1198 }
1199 } else if (num == 1 && mf == -1) {
1200 snprintf(fn, sizeof(fn), "digits/%dF", num);
1201 num = 0;
1202 } else if (num < 20) {
1203 snprintf(fn, sizeof(fn), "digits/%d", num);
1204 num = 0;
1205 } else if (num < 100) {
1206 int ones = num % 10;
1207 if (ones) {
1208 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1209 num -= ones;
1210 } else {
1211 snprintf(fn, sizeof(fn), "digits/%d", num);
1212 num = 0;
1213 }
1214 } else if (num == 100 && t == 0) {
1215 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1216 num = 0;
1217 } else if (num < 1000) {
1218 int hundreds = num / 100;
1219 num = num % 100;
1220 if (hundreds == 1) {
1221 ast_copy_string(fn, "digits/1N", sizeof(fn));
1222 } else {
1223 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1224 }
1225 ast_copy_string(fna, "digits/hundred", sizeof(fna));
1226 } else if (num == 1000 && t == 0) {
1227 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1228 num = 0;
1229 } else if (num < 1000000) {
1230 int thousands = num / 1000;
1231 num = num % 1000;
1232 t = 1;
1233 if (thousands == 1) {
1234 ast_copy_string(fn, "digits/1N", sizeof(fn));
1235 ast_copy_string(fna, "digits/thousand", sizeof(fna));
1236 } else {
1237 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
1238 if (res)
1239 return res;
1240 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1241 }
1242 } else if (num < 1000000000) {
1243 int millions = num / 1000000;
1244 num = num % 1000000;
1245 t = 1;
1246 if (millions == 1) {
1247 ast_copy_string(fn, "digits/1F", sizeof(fn));
1248 ast_copy_string(fna, "digits/million", sizeof(fna));
1249 } else {
1250 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
1251 if (res)
1252 return res;
1253 ast_copy_string(fn, "digits/millions", sizeof(fn));
1254 }
1255 } else if (num <= INT_MAX) {
1256 int billions = num / 1000000000;
1257 num = num % 1000000000;
1258 t = 1;
1259 if (billions == 1) {
1260 ast_copy_string(fn, "digits/1F", sizeof(fn));
1261 ast_copy_string(fna, "digits/milliard", sizeof(fna));
1262 } else {
1263 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
1264 if (res) {
1265 return res;
1266 }
1267 ast_copy_string(fn, "digits/milliards", sizeof(fn));
1268 }
1269 } else {
1270 ast_debug(1, "Number '%d' is too big for me\n", num);
1271 res = -1;
1272 }
1273 if (!res) {
1274 if (!ast_streamfile(chan, fn, language)) {
1275 if ((audiofd > -1) && (ctrlfd > -1))
1276 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1277 else
1278 res = ast_waitstream(chan, ints);
1279 }
1280 ast_stopstream(chan);
1281 if (!res) {
1282 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1283 if ((audiofd > -1) && (ctrlfd > -1))
1284 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1285 else
1286 res = ast_waitstream(chan, ints);
1287 }
1288 ast_stopstream(chan);
1289 strcpy(fna, "");
1290 }
1291 }
1292 }
1293 return res;
1294}
1295
1296/*! \brief ast_say_number_full_en_GB: British syntax
1297 New files:
1298 - In addition to American English, the following sounds are required: "vm-and"
1299 */
1300static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1301{
1302 int res = 0;
1303 int playh = 0;
1304 int playa = 0;
1305 char fn[256] = "";
1306 if (!num)
1307 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1308
1309 while (!res && (num || playh || playa )) {
1310 if (num < 0) {
1311 ast_copy_string(fn, "digits/minus", sizeof(fn));
1312 if ( num > INT_MIN ) {
1313 num = -num;
1314 } else {
1315 num = 0;
1316 }
1317 } else if (playh) {
1318 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1319 playh = 0;
1320 } else if (playa) {
1321 ast_copy_string(fn, "vm-and", sizeof(fn));
1322 playa = 0;
1323 } else if (num < 20) {
1324 snprintf(fn, sizeof(fn), "digits/%d", num);
1325 num = 0;
1326 } else if (num < 100) {
1327 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1328 num %= 10;
1329 } else if (num < 1000) {
1330 int hundreds = num / 100;
1331 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1332
1333 playh++;
1334 num -= 100 * hundreds;
1335 if (num)
1336 playa++;
1337 } else if (num < 1000000) {
1338 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
1339 if (res)
1340 return res;
1341 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1342 num %= 1000;
1343 if (num && num < 100)
1344 playa++;
1345 } else if (num < 1000000000) {
1346 int millions = num / 1000000;
1347 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1348 if (res)
1349 return res;
1350 ast_copy_string(fn, "digits/million", sizeof(fn));
1351 num %= 1000000;
1352 if (num && num < 100)
1353 playa++;
1354 } else {
1355 ast_debug(1, "Number '%d' is too big for me\n", num);
1356 res = -1;
1357 }
1358
1359 if (!res) {
1360 if (!ast_streamfile(chan, fn, language)) {
1361 if ((audiofd > -1) && (ctrlfd > -1))
1362 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1363 else
1364 res = ast_waitstream(chan, ints);
1365 }
1366 ast_stopstream(chan);
1367 }
1368 }
1369 return res;
1370}
1371
1372/*! \brief ast_say_number_full_es: Spanish syntax
1373
1374 New files:
1375 Requires a few new audios:
1376 1F.gsm: feminine 'una'
1377 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
1378 */
1379static 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)
1380{
1381 int res = 0;
1382 int playa = 0;
1383 int mf = 0; /* +1 = male; -1 = female */
1384 char fn[256] = "";
1385 if (!num)
1386 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1387
1388 if (options) {
1389 if (!strncasecmp(options, "f", 1))
1390 mf = -1;
1391 else if (!strncasecmp(options, "m", 1))
1392 mf = 1;
1393 }
1394
1395 while (!res && num) {
1396 if (num < 0) {
1397 ast_copy_string(fn, "digits/minus", sizeof(fn));
1398 if ( num > INT_MIN ) {
1399 num = -num;
1400 } else {
1401 num = 0;
1402 }
1403 } else if (playa) {
1404 ast_copy_string(fn, "digits/and", sizeof(fn));
1405 playa = 0;
1406 } else if (num == 1) {
1407 if (mf < 0)
1408 snprintf(fn, sizeof(fn), "digits/%dF", num);
1409 else if (mf > 0)
1410 snprintf(fn, sizeof(fn), "digits/%dM", num);
1411 else
1412 snprintf(fn, sizeof(fn), "digits/%d", num);
1413 num = 0;
1414 } else if (num < 31) {
1415 snprintf(fn, sizeof(fn), "digits/%d", num);
1416 num = 0;
1417 } else if (num < 100) {
1418 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1419 num %= 10;
1420 if (num)
1421 playa++;
1422 } else if (num == 100) {
1423 ast_copy_string(fn, "digits/100", sizeof(fn));
1424 num = 0;
1425 } else if (num < 200) {
1426 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1427 num -= 100;
1428 } else {
1429 if (num < 1000) {
1430 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1431 num %= 100;
1432 } else if (num < 2000) {
1433 num %= 1000;
1434 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1435 } else {
1436 if (num < 1000000) {
1437 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1438 if (res)
1439 return res;
1440 num %= 1000;
1441 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1442 } else {
1443 if (num < 2147483640) {
1444 if ((num/1000000) == 1) {
1445 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1446 if (res)
1447 return res;
1448 ast_copy_string(fn, "digits/million", sizeof(fn));
1449 } else {
1450 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1451 if (res)
1452 return res;
1453 ast_copy_string(fn, "digits/millions", sizeof(fn));
1454 }
1455 num %= 1000000;
1456 } else {
1457 ast_debug(1, "Number '%d' is too big for me\n", num);
1458 res = -1;
1459 }
1460 }
1461 }
1462 }
1463
1464 if (!res) {
1465 if (!ast_streamfile(chan, fn, language)) {
1466 if ((audiofd > -1) && (ctrlfd > -1))
1467 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1468 else
1469 res = ast_waitstream(chan, ints);
1470 }
1471 ast_stopstream(chan);
1472
1473 }
1474
1475 }
1476 return res;
1477}
1478
1479/*! \brief ast_say_number_full_fr: French syntax
1480 Extra sounds needed:
1481 1F: feminin 'une'
1482 et: 'and' */
1483static 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)
1484{
1485 int res = 0;
1486 int playh = 0;
1487 int playa = 0;
1488 int mf = 1; /* +1 = male; -1 = female */
1489 char fn[256] = "";
1490 if (!num)
1491 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1492
1493 if (options && !strncasecmp(options, "f", 1))
1494 mf = -1;
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, "digits/et", sizeof(fn));
1509 playa = 0;
1510 } else if (num == 1) {
1511 if (mf < 0)
1512 snprintf(fn, sizeof(fn), "digits/%dF", num);
1513 else
1514 snprintf(fn, sizeof(fn), "digits/%d", num);
1515 num = 0;
1516 } else if (num < 21) {
1517 snprintf(fn, sizeof(fn), "digits/%d", num);
1518 num = 0;
1519 } else if (num < 70) {
1520 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1521 if ((num % 10) == 1) playa++;
1522 num = num % 10;
1523 } else if (num < 80) {
1524 ast_copy_string(fn, "digits/60", sizeof(fn));
1525 if ((num % 10) == 1) playa++;
1526 num -= 60;
1527 } else if (num < 100) {
1528 ast_copy_string(fn, "digits/80", sizeof(fn));
1529 num = num - 80;
1530 } else if (num < 200) {
1531 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1532 num = num - 100;
1533 } else if (num < 1000) {
1534 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1535 playh++;
1536 num = num % 100;
1537 } else if (num < 2000) {
1538 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1539 num = num - 1000;
1540 } else if (num < 1000000) {
1541 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1542 if (res)
1543 return res;
1544 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1545 num = num % 1000;
1546 } else if (num < 1000000000) {
1547 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1548 if (res)
1549 return res;
1550 ast_copy_string(fn, "digits/million", sizeof(fn));
1551 num = num % 1000000;
1552 } else {
1553 ast_debug(1, "Number '%d' is too big for me\n", num);
1554 res = -1;
1555 }
1556 if (!res) {
1557 if (!ast_streamfile(chan, fn, language)) {
1558 if ((audiofd > -1) && (ctrlfd > -1))
1559 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1560 else
1561 res = ast_waitstream(chan, ints);
1562 }
1563 ast_stopstream(chan);
1564 }
1565 }
1566 return res;
1567}
1568
1569
1570
1571/* Hebrew syntax
1572 * Check doc/lang/hebrew-digits.txt for information about the various
1573 * recordings required to make this translation work properly */
1574#define SAY_NUM_BUF_SIZE 256
1575static 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)
1576{
1577 int res = 0;
1578 int state = 0; /* no need to save anything */
1579 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1580 int tmpnum = 0;
1581
1582 char fn[SAY_NUM_BUF_SIZE] = "";
1583
1584 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1585
1586 if (!num) {
1587 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1588 }
1589 if (options && !strncasecmp(options, "m", 1)) {
1590 mf = 1;
1591 }
1592 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1593
1594 /* Do we have work to do? */
1595 while (!res && (num || (state > 0))) {
1596 /* first type of work: play a second sound. In this loop
1597 * we can only play one sound file at a time. Thus playing
1598 * a second one requires repeating the loop just for the
1599 * second file. The variable 'state' remembers where we were.
1600 * state==0 is the normal mode and it means that we continue
1601 * to check if the number num has yet anything left.
1602 */
1603 ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1604
1605 if (state == 1) {
1606 state = 0;
1607 } else if (state == 2) {
1608 if ((num >= 11) && (num < 21)) {
1609 if (mf < 0) {
1610 snprintf(fn, sizeof(fn), "digits/ve");
1611 } else {
1612 snprintf(fn, sizeof(fn), "digits/uu");
1613 }
1614 } else {
1615 switch (num) {
1616 case 1:
1617 snprintf(fn, sizeof(fn), "digits/ve");
1618 break;
1619 case 2:
1620 snprintf(fn, sizeof(fn), "digits/uu");
1621 break;
1622 case 3:
1623 if (mf < 0) {
1624 snprintf(fn, sizeof(fn), "digits/ve");
1625 } else {
1626 snprintf(fn, sizeof(fn), "digits/uu");
1627 }
1628 break;
1629 case 4:
1630 snprintf(fn, sizeof(fn), "digits/ve");
1631 break;
1632 case 5:
1633 snprintf(fn, sizeof(fn), "digits/ve");
1634 break;
1635 case 6:
1636 snprintf(fn, sizeof(fn), "digits/ve");
1637 break;
1638 case 7:
1639 snprintf(fn, sizeof(fn), "digits/ve");
1640 break;
1641 case 8:
1642 snprintf(fn, sizeof(fn), "digits/uu");
1643 break;
1644 case 9:
1645 snprintf(fn, sizeof(fn), "digits/ve");
1646 break;
1647 case 10:
1648 snprintf(fn, sizeof(fn), "digits/ve");
1649 break;
1650 }
1651 }
1652 state = 0;
1653 } else if (state == 3) {
1654 snprintf(fn, sizeof(fn), "digits/1k");
1655 state = 0;
1656 } else if (num < 0) {
1657 snprintf(fn, sizeof(fn), "digits/minus");
1658 num = (-1) * num;
1659 } else if (num < 20) {
1660 if (mf < 0) {
1661 snprintf(fn, sizeof(fn), "digits/%d", num);
1662 } else {
1663 snprintf(fn, sizeof(fn), "digits/%dm", num);
1664 }
1665 num = 0;
1666 } else if ((num < 100) && (num >= 20)) {
1667 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1668 num = num % 10;
1669 if (num > 0) {
1670 state = 2;
1671 }
1672 } else if ((num >= 100) && (num < 1000)) {
1673 tmpnum = num / 100;
1674 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1675 num = num - (tmpnum * 100);
1676 if ((num > 0) && (num < 11)) {
1677 state = 2;
1678 }
1679 } else if ((num >= 1000) && (num < 10000)) {
1680 tmpnum = num / 1000;
1681 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1682 num = num - (tmpnum * 1000);
1683 if ((num > 0) && (num < 11)) {
1684 state = 2;
1685 }
1686 } else if (num < 20000) {
1687 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1688 num = num % 1000;
1689 state = 3;
1690 } else if (num < 1000000) {
1691 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1692 if (res) {
1693 return res;
1694 }
1695 snprintf(fn, sizeof(fn), "digits/1k");
1696 num = num % 1000;
1697 if ((num > 0) && (num < 11)) {
1698 state = 2;
1699 }
1700 } else if (num < 2000000) {
1701 snprintf(fn, sizeof(fn), "digits/million");
1702 num = num % 1000000;
1703 if ((num > 0) && (num < 11)) {
1704 state = 2;
1705 }
1706 } else if (num < 3000000) {
1707 snprintf(fn, sizeof(fn), "digits/twomillion");
1708 num = num - 2000000;
1709 if ((num > 0) && (num < 11)) {
1710 state = 2;
1711 }
1712 } else if (num < 1000000000) {
1713 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1714 if (res) {
1715 return res;
1716 }
1717 snprintf(fn, sizeof(fn), "digits/million");
1718 num = num % 1000000;
1719 if ((num > 0) && (num < 11)) {
1720 state = 2;
1721 }
1722 } else {
1723 ast_debug(1, "Number '%d' is too big for me\n", num);
1724 res = -1;
1725 }
1726 tmpnum = 0;
1727 if (!res) {
1728 if (!ast_streamfile(chan, fn, language)) {
1729 if ((audiofd > -1) && (ctrlfd > -1)) {
1730 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1731 } else {
1732 res = ast_waitstream(chan, ints);
1733 }
1734 }
1735 ast_stopstream(chan);
1736 }
1737 }
1738 return res;
1739}
1740
1741/*! \brief ast_say_number_full_hu: Hungarian syntax
1742
1743 Extra sounds needed:
1744 10en: "tizen"
1745 20on: "huszon"
1746*/
1747static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1748{
1749 int res = 0;
1750 int playh = 0;
1751 char fn[256] = "";
1752 if (!num)
1753 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1754
1755 /*
1756 Hungarian support
1757 like english, except numbers up to 29 are from 2 words.
1758 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1759 */
1760
1761 while(!res && (num || playh)) {
1762 if (num < 0) {
1763 ast_copy_string(fn, "digits/minus", sizeof(fn));
1764 if ( num > INT_MIN ) {
1765 num = -num;
1766 } else {
1767 num = 0;
1768 }
1769 } else if (playh) {
1770 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1771 playh = 0;
1772 } else if (num < 11 || num == 20) {
1773 snprintf(fn, sizeof(fn), "digits/%d", num);
1774 num = 0;
1775 } else if (num < 20) {
1776 ast_copy_string(fn, "digits/10en", sizeof(fn));
1777 num -= 10;
1778 } else if (num < 30) {
1779 ast_copy_string(fn, "digits/20on", sizeof(fn));
1780 num -= 20;
1781 } else if (num < 100) {
1782 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1783 num %= 10;
1784 } else {
1785 if (num < 1000){
1786 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1787 playh++;
1788 num %= 100;
1789 } else {
1790 if (num < 1000000) { /* 1,000,000 */
1791 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1792 if (res)
1793 return res;
1794 num %= 1000;
1795 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1796 } else {
1797 if (num < 1000000000) { /* 1,000,000,000 */
1798 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1799 if (res)
1800 return res;
1801 num %= 1000000;
1802 ast_copy_string(fn, "digits/million", sizeof(fn));
1803 } else {
1804 ast_debug(1, "Number '%d' is too big for me\n", num);
1805 res = -1;
1806 }
1807 }
1808 }
1809 }
1810 if (!res) {
1811 if(!ast_streamfile(chan, fn, language)) {
1812 if ((audiofd > -1) && (ctrlfd > -1))
1813 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1814 else
1815 res = ast_waitstream(chan, ints);
1816 }
1817 ast_stopstream(chan);
1818 }
1819 }
1820 return res;
1821}
1822
1823/*! \brief ast_say_number_full_is: Icelandic syntax */
1824/* New files:
1825 In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk"
1826 */
1827static 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)
1828{
1829 int res = 0;
1830 int playh = 0;
1831 int playa = 0;
1832 int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */
1833 char fn[256] = "";
1834
1835 if (!num)
1836 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1837
1838 if (options && !strncasecmp(options, "f", 1)) cn = 2;
1839 if (options && !strncasecmp(options, "c", 1)) cn = 3;
1840 /* It seems that sometimes people are using c and sometimes n. */
1841 if (options && !strncasecmp(options, "n", 1)) cn = 3;
1842
1843 while (!res && (num || playh || playa )) {
1844 if (num < 0) {
1845 ast_copy_string(fn, "digits/minus", sizeof(fn));
1846 if ( num > INT_MIN ) {
1847 num = -num;
1848 } else {
1849 num = 0;
1850 }
1851 } else if (playh) {
1852 if (playh > 1)
1853 ast_copy_string(fn, "digits/hundreds", sizeof(fn));
1854 else
1855 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1856 playh = 0;
1857 } else if (playa) {
1858 ast_copy_string(fn, "digits/and", sizeof(fn));
1859 playa = 0;
1860 } else if (num < 5 && cn == 2) {
1861 snprintf(fn, sizeof(fn), "digits/%dkvk", num);
1862 num = 0;
1863 } else if (num < 5 && cn == 3) {
1864 snprintf(fn, sizeof(fn), "digits/%dhk", num);
1865 num = 0;
1866 } else if (num < 20) {
1867 snprintf(fn, sizeof(fn), "digits/%d", num);
1868 num = 0;
1869 } else if (num < 100) {
1870 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1871 num %= 10;
1872 if (num)
1873 playa++;
1874 } else if (num < 1000) {
1875 int hundreds = num / 100;
1876 /* The number prepending hundreds are in neutral */
1877 if (hundreds < 5)
1878 snprintf(fn, sizeof(fn), "digits/%dhk", hundreds);
1879 else
1880 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1881
1882 playh = hundreds;
1883 num -= 100 * hundreds;
1884 if (num && num < 20)
1885 playa++;
1886 /* The 'and' moves forward on even tens. */
1887 if (num && (num % 10) == 0)
1888 playa++;
1889 } else if (num < 1000000) {
1890 res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1891 /* Play 'and' if it's an even hundred. */
1892 if ((num % 100) == 0 && (num % 1000 != 0)) {
1893 playa++;
1894 }
1895 if (res)
1896 return res;
1897 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1898 num %= 1000;
1899 if (num && (num < 20 || (num % 10 == 0)))
1900 playa++;
1901 } else if (num < 1000000000) {
1902 int millions = num / 1000000;
1903 /* The number of millions is feminine */
1904 res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd);
1905 if (res)
1906 return res;
1907 if (millions > 1)
1908 ast_copy_string(fn, "digits/millions", sizeof(fn));
1909 else
1910 ast_copy_string(fn, "digits/million", sizeof(fn));
1911 num %= 1000000;
1912 if (num && num < 100)
1913 playa++;
1914 } else if (num < INT_MAX) {
1915 int milliards = num / 1000000000;
1916 /* The number of milliards is masculine */
1917 res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd);
1918 if (res)
1919 return res;
1920 if (milliards > 1)
1921 ast_copy_string(fn, "digits/milliards", sizeof(fn));
1922 else
1923 ast_copy_string(fn, "digits/milliard", sizeof(fn));
1924 num %= 1000000000;
1925 if (num && num < 100)
1926 playa++;
1927 } else {
1928 ast_debug(1, "Number '%d' is too big for me\n", num);
1929 res = -1;
1930 }
1931
1932 if (!res) {
1933 if (!ast_streamfile(chan, fn, language)) {
1934 if ((audiofd > -1) && (ctrlfd > -1))
1935 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1936 else
1937 res = ast_waitstream(chan, ints);
1938 }
1939 ast_stopstream(chan);
1940 }
1941 }
1942 return res;
1943}
1944
1945
1946/*! \brief ast_say_number_full_it: Italian */
1947static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1948{
1949 int res = 0;
1950 int playh = 0;
1951 int tempnum = 0;
1952 char fn[256] = "";
1953
1954 if (!num)
1955 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1956
1957 /*
1958 Italian support
1959
1960 Like english, numbers up to 20 are a single 'word', and others
1961 compound, but with exceptions.
1962 For example 21 is not twenty-one, but there is a single word in 'it'.
1963 Idem for 28 (ie when a the 2nd part of a compound number
1964 starts with a vowel)
1965
1966 There are exceptions also for hundred, thousand and million.
1967 In english 100 = one hundred, 200 is two hundred.
1968 In italian 100 = cento , like to say hundred (without one),
1969 200 and more are like english.
1970
1971 Same applies for thousand:
1972 1000 is one thousand in en, 2000 is two thousand.
1973 In it we have 1000 = mille , 2000 = 2 mila
1974
1975 For million(s) we use the plural, if more than one
1976 Also, one million is abbreviated in it, like on-million,
1977 or 'un milione', not 'uno milione'.
1978 So the right file is provided.
1979 */
1980
1981 while (!res && (num || playh)) {
1982 if (num < 0) {
1983 ast_copy_string(fn, "digits/minus", sizeof(fn));
1984 if ( num > INT_MIN ) {
1985 num = -num;
1986 } else {
1987 num = 0;
1988 }
1989 } else if (playh) {
1990 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1991 playh = 0;
1992 } else if (num < 20) {
1993 snprintf(fn, sizeof(fn), "digits/%d", num);
1994 num = 0;
1995 } else if (num == 21) {
1996 snprintf(fn, sizeof(fn), "digits/%d", num);
1997 num = 0;
1998 } else if (num == 28) {
1999 snprintf(fn, sizeof(fn), "digits/%d", num);
2000 num = 0;
2001 } else if (num == 31) {
2002 snprintf(fn, sizeof(fn), "digits/%d", num);
2003 num = 0;
2004 } else if (num == 38) {
2005 snprintf(fn, sizeof(fn), "digits/%d", num);
2006 num = 0;
2007 } else if (num == 41) {
2008 snprintf(fn, sizeof(fn), "digits/%d", num);
2009 num = 0;
2010 } else if (num == 48) {
2011 snprintf(fn, sizeof(fn), "digits/%d", num);
2012 num = 0;
2013 } else if (num == 51) {
2014 snprintf(fn, sizeof(fn), "digits/%d", num);
2015 num = 0;
2016 } else if (num == 58) {
2017 snprintf(fn, sizeof(fn), "digits/%d", num);
2018 num = 0;
2019 } else if (num == 61) {
2020 snprintf(fn, sizeof(fn), "digits/%d", num);
2021 num = 0;
2022 } else if (num == 68) {
2023 snprintf(fn, sizeof(fn), "digits/%d", num);
2024 num = 0;
2025 } else if (num == 71) {
2026 snprintf(fn, sizeof(fn), "digits/%d", num);
2027 num = 0;
2028 } else if (num == 78) {
2029 snprintf(fn, sizeof(fn), "digits/%d", num);
2030 num = 0;
2031 } else if (num == 81) {
2032 snprintf(fn, sizeof(fn), "digits/%d", num);
2033 num = 0;
2034 } else if (num == 88) {
2035 snprintf(fn, sizeof(fn), "digits/%d", num);
2036 num = 0;
2037 } else if (num == 91) {
2038 snprintf(fn, sizeof(fn), "digits/%d", num);
2039 num = 0;
2040 } else if (num == 98) {
2041 snprintf(fn, sizeof(fn), "digits/%d", num);
2042 num = 0;
2043 } else if (num < 100) {
2044 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2045 num %= 10;
2046 } else {
2047 if (num < 1000) {
2048 if ((num / 100) > 1) {
2049 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2050 playh++;
2051 } else {
2052 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2053 }
2054 num %= 100;
2055 } else {
2056 if (num < 1000000) { /* 1,000,000 */
2057 if ((num/1000) > 1)
2058 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
2059 if (res)
2060 return res;
2061 tempnum = num;
2062 num %= 1000;
2063 if ((tempnum / 1000) < 2)
2064 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2065 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
2066 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2067 } else {
2068 if (num < 1000000000) { /* 1,000,000,000 */
2069 if ((num / 1000000) > 1)
2070 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2071 if (res)
2072 return res;
2073 tempnum = num;
2074 num %= 1000000;
2075 if ((tempnum / 1000000) < 2)
2076 ast_copy_string(fn, "digits/million", sizeof(fn));
2077 else
2078 ast_copy_string(fn, "digits/millions", sizeof(fn));
2079 } else {
2080 ast_debug(1, "Number '%d' is too big for me\n", num);
2081 res = -1;
2082 }
2083 }
2084 }
2085 }
2086 if (!res) {
2087 if (!ast_streamfile(chan, fn, language)) {
2088 if ((audiofd > -1) && (ctrlfd > -1))
2089 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2090 else
2091 res = ast_waitstream(chan, ints);
2092 }
2093 ast_stopstream(chan);
2094 }
2095 }
2096 return res;
2097}
2098
2099/*! \brief ast_say_number_full_nl: dutch syntax
2100 * New files: digits/nl-en
2101 */
2102static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2103{
2104 int res = 0;
2105 int playh = 0;
2106 int units = 0;
2107 char fn[256] = "";
2108 if (!num)
2109 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2110 while (!res && (num || playh )) {
2111 if (num < 0) {
2112 ast_copy_string(fn, "digits/minus", sizeof(fn));
2113 if ( num > INT_MIN ) {
2114 num = -num;
2115 } else {
2116 num = 0;
2117 }
2118 } else if (playh) {
2119 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2120 playh = 0;
2121 } else if (num < 20) {
2122 snprintf(fn, sizeof(fn), "digits/%d", num);
2123 num = 0;
2124 } else if (num < 100) {
2125 units = num % 10;
2126 if (units > 0) {
2127 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
2128 if (res)
2129 return res;
2130 num = num - units;
2131 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
2132 } else {
2133 snprintf(fn, sizeof(fn), "digits/%d", num - units);
2134 num = 0;
2135 }
2136 } else if (num < 200) {
2137 /* hundred, not one-hundred */
2138 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2139 num %= 100;
2140 } else if (num < 1000) {
2141 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
2142 playh++;
2143 num %= 100;
2144 } else {
2145 if (num < 1100) {
2146 /* thousand, not one-thousand */
2147 num %= 1000;
2148 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2149 } else if (num < 10000) { /* 1,100 to 9,9999 */
2150 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
2151 if (res)
2152 return res;
2153 num %= 100;
2154 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2155 } else {
2156 if (num < 1000000) { /* 1,000,000 */
2157 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
2158 if (res)
2159 return res;
2160 num %= 1000;
2161 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2162 } else {
2163 if (num < 1000000000) { /* 1,000,000,000 */
2164 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2165 if (res)
2166 return res;
2167 num %= 1000000;
2168 ast_copy_string(fn, "digits/million", sizeof(fn));
2169 } else {
2170 ast_debug(1, "Number '%d' is too big for me\n", num);
2171 res = -1;
2172 }
2173 }
2174 }
2175 }
2176
2177 if (!res) {
2178 if (!ast_streamfile(chan, fn, language)) {
2179 if ((audiofd > -1) && (ctrlfd > -1))
2180 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2181 else
2182 res = ast_waitstream(chan, ints);
2183 }
2184 ast_stopstream(chan);
2185 }
2186 }
2187 return res;
2188}
2189
2190/*! \brief ast_say_number_full_no: Norwegian syntax
2191 * New files:
2192 * In addition to American English, the following sounds are required: "and", "1N"
2193 *
2194 * The grammar for Norwegian numbers is the same as for English except
2195 * for the following:
2196 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
2197 * "and" before the last two digits, i.e. 2034 is "two thousand and
2198 * thirty-four" and 1000012 is "one million and twelve".
2199 */
2200static 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)
2201{
2202 int res = 0;
2203 int playh = 0;
2204 int playa = 0;
2205 int cn = 1; /* +1 = commune; -1 = neuter */
2206 char fn[256] = "";
2207
2208 if (!num)
2209 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2210
2211 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2212
2213 while (!res && (num || playh || playa )) {
2214 if (num < 0) {
2215 ast_copy_string(fn, "digits/minus", sizeof(fn));
2216 if ( num > INT_MIN ) {
2217 num = -num;
2218 } else {
2219 num = 0;
2220 }
2221 } else if (playh) {
2222 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2223 playh = 0;
2224 } else if (playa) {
2225 ast_copy_string(fn, "digits/and", sizeof(fn));
2226 playa = 0;
2227 } else if (num == 1 && cn == -1) {
2228 ast_copy_string(fn, "digits/1N", sizeof(fn));
2229 num = 0;
2230 } else if (num < 20) {
2231 snprintf(fn, sizeof(fn), "digits/%d", num);
2232 num = 0;
2233 } else if (num < 100) {
2234 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2235 num %= 10;
2236 } else if (num < 1000) {
2237 int hundreds = num / 100;
2238 if (hundreds == 1)
2239 ast_copy_string(fn, "digits/1N", sizeof(fn));
2240 else
2241 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2242
2243 playh++;
2244 num -= 100 * hundreds;
2245 if (num)
2246 playa++;
2247 } else if (num < 1000000) {
2248 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2249 if (res)
2250 return res;
2251 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2252 num %= 1000;
2253 if (num && num < 100)
2254 playa++;
2255 } else if (num < 1000000000) {
2256 int millions = num / 1000000;
2257 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
2258 if (res)
2259 return res;
2260 ast_copy_string(fn, "digits/million", sizeof(fn));
2261 num %= 1000000;
2262 if (num && num < 100)
2263 playa++;
2264 } else {
2265 ast_debug(1, "Number '%d' is too big for me\n", num);
2266 res = -1;
2267 }
2268
2269 if (!res) {
2270 if (!ast_streamfile(chan, fn, language)) {
2271 if ((audiofd > -1) && (ctrlfd > -1))
2272 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2273 else
2274 res = ast_waitstream(chan, ints);
2275 }
2276 ast_stopstream(chan);
2277 }
2278 }
2279 return res;
2280}
2281
2282typedef struct {
2284 char *cyfry[10];
2285 char *cyfry2[10];
2286 char *setki[10];
2287 char *dziesiatki[10];
2288 char *nastki[10];
2289 char *rzedy[3][3];
2290} odmiana;
2291
2292static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
2293{
2294 if (rzad==0)
2295 return "";
2296
2297 if (i==1)
2298 return odm->rzedy[rzad - 1][0];
2299 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
2300 return odm->rzedy[rzad - 1][1];
2301 else
2302 return odm->rzedy[rzad - 1][2];
2303}
2304
2305static char* pl_append(char* buffer, char* str)
2306{
2307 strcpy(buffer, str);
2308 buffer += strlen(str);
2309 return buffer;
2310}
2311
2312static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
2313{
2314 char file_name[255] = "digits/";
2315 strcat(file_name, fn);
2316 ast_debug(1, "Trying to play: %s\n", file_name);
2317 if (!ast_streamfile(chan, file_name, language)) {
2318 if ((audiofd > -1) && (ctrlfd > -1))
2319 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2320 else
2321 ast_waitstream(chan, ints);
2322 }
2323 ast_stopstream(chan);
2324}
2325
2326static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
2327{
2328 /* Initialise variables to allow compilation on Debian-stable, etc */
2329 int m1000E6 = 0;
2330 int i1000E6 = 0;
2331 int m1000E3 = 0;
2332 int i1000E3 = 0;
2333 int m1000 = 0;
2334 int i1000 = 0;
2335 int m100 = 0;
2336 int i100 = 0;
2337
2338 if (i == 0 && rzad > 0) {
2339 return;
2340 }
2341 if (i == 0) {
2342 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
2343 return;
2344 }
2345
2346 m1000E6 = i % 1000000000;
2347 i1000E6 = i / 1000000000;
2348
2349 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
2350
2351 m1000E3 = m1000E6 % 1000000;
2352 i1000E3 = m1000E6 / 1000000;
2353
2354 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
2355
2356 m1000 = m1000E3 % 1000;
2357 i1000 = m1000E3 / 1000;
2358
2359 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
2360
2361 m100 = m1000 % 100;
2362 i100 = m1000 / 100;
2363
2364 if (i100>0)
2365 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
2366
2367 if (m100 > 0 && m100 <= 9) {
2368 if (m1000 > 0)
2369 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
2370 else
2371 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
2372 } else if (m100 % 10 == 0 && m100 != 0) {
2373 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2374 } else if (m100 > 10 && m100 <= 19) {
2375 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
2376 } else if (m100 > 20) {
2377 if (odm->separator_dziesiatek[0] == ' ') {
2378 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2379 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
2380 } else {
2381 char buf[10];
2382 char *b = buf;
2383 b = pl_append(b, odm->dziesiatki[m100 / 10]);
2385 pl_append(b, odm->cyfry2[m100 % 10]);
2386 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
2387 }
2388 }
2389
2390 if (rzad > 0) {
2391 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
2392 }
2393}
2394
2395/* ast_say_number_full_pl: Polish syntax
2396
2397Sounds needed:
23980 zero
23991 jeden
240010 dziesiec
2401100 sto
24021000 tysiac
24031000000 milion
24041000000000 miliard
24051000000000.2 miliardy
24061000000000.5 miliardow
24071000000.2 miliony
24081000000.5 milionow
24091000.2 tysiace
24101000.5 tysiecy
2411100m stu
241210m dziesieciu
241311 jedenascie
241411m jedenastu
241512 dwanascie
241612m dwunastu
241713 trzynascie
241813m trzynastu
241914 czternascie
242014m czternastu
242115 pietnascie
242215m pietnastu
242316 szesnascie
242416m szesnastu
242517 siedemnascie
242617m siedemnastu
242718 osiemnascie
242818m osiemnastu
242919 dziewietnascie
243019m dziewietnastu
24311z jedna
24322 dwa
243320 dwadziescia
2434200 dwiescie
2435200m dwustu
243620m dwudziestu
24372-1m dwaj
24382-2m dwoch
24392z dwie
24403 trzy
244130 trzydziesci
2442300 trzysta
2443300m trzystu
244430m trzydziestu
24453-1m trzej
24463-2m trzech
24474 cztery
244840 czterdziesci
2449400 czterysta
2450400m czterystu
245140m czterdziestu
24524-1m czterej
24534-2m czterech
24545 piec
245550 piecdziesiat
2456500 piecset
2457500m pieciuset
245850m piedziesieciu
24595m pieciu
24606 szesc
246160 szescdziesiat
2462600 szescset
2463600m szesciuset
246460m szescdziesieciu
24656m szesciu
24667 siedem
246770 siedemdziesiat
2468700 siedemset
2469700m siedmiuset
247070m siedemdziesieciu
24717m siedmiu
24728 osiem
247380 osiemdziesiat
2474800 osiemset
2475800m osmiuset
247680m osiemdziesieciu
24778m osmiu
24789 dziewiec
247990 dziewiecdziesiat
2480900 dziewiecset
2481900m dziewieciuset
248290m dziewiedziesieciu
24839m dziewieciu
2484and combinations of eg.: 20_1, 30m_3m, etc...
2485
2486*/
2487static 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)
2488{
2489 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2490
2491 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2492
2493 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2494
2495 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2496
2497 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2498
2499 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2500
2501 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2502
2503 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2504
2505 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2506
2507 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2508
2509 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2510
2511 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2512
2513 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2514
2515 /* Initialise variables to allow compilation on Debian-stable, etc */
2516 odmiana *o;
2517
2518 static odmiana *odmiana_nieosobowa = NULL;
2519 static odmiana *odmiana_meska = NULL;
2520 static odmiana *odmiana_zenska = NULL;
2521
2522 if (odmiana_nieosobowa == NULL) {
2523 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2524
2525 odmiana_nieosobowa->separator_dziesiatek = " ";
2526
2527 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2528 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2529 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2530 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2531 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2532 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2533 }
2534
2535 if (odmiana_zenska == NULL) {
2536 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2537
2538 odmiana_zenska->separator_dziesiatek = " ";
2539
2540 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2541 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2542 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2543 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2544 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2545 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2546 }
2547
2548 if (odmiana_meska == NULL) {
2549 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2550
2551 odmiana_meska->separator_dziesiatek = " ";
2552
2553 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2554 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2555 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2556 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2557 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2558 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2559 }
2560
2561 if (options) {
2562 if (strncasecmp(options, "f", 1) == 0)
2563 o = odmiana_zenska;
2564 else if (strncasecmp(options, "m", 1) == 0)
2565 o = odmiana_meska;
2566 else
2567 o = odmiana_nieosobowa;
2568 } else
2569 o = odmiana_nieosobowa;
2570
2571 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2572 return 0;
2573}
2574
2575/* ast_say_number_full_pt: Portuguese syntax
2576
2577 * Extra sounds needed:
2578 * For feminin all sound files ends with F
2579 * 100E for 100+ something
2580 * 1000000S for plural
2581 * pt-e for 'and'
2582 */
2583static 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)
2584{
2585 int res = 0;
2586 int playh = 0;
2587 int mf = 1; /* +1 = male; -1 = female */
2588 char fn[256] = "";
2589
2590 if (!num)
2591 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2592
2593 if (options && !strncasecmp(options, "f", 1))
2594 mf = -1;
2595
2596 while (!res && num ) {
2597 if (num < 0) {
2598 ast_copy_string(fn, "digits/minus", sizeof(fn));
2599 if ( num > INT_MIN ) {
2600 num = -num;
2601 } else {
2602 num = 0;
2603 }
2604 } else if (num < 20) {
2605 if ((num == 1 || num == 2) && (mf < 0))
2606 snprintf(fn, sizeof(fn), "digits/%dF", num);
2607 else
2608 snprintf(fn, sizeof(fn), "digits/%d", num);
2609 num = 0;
2610 } else if (num < 100) {
2611 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2612 if (num % 10)
2613 playh = 1;
2614 num = num % 10;
2615 } else if (num < 1000) {
2616 if (num == 100)
2617 ast_copy_string(fn, "digits/100", sizeof(fn));
2618 else if (num < 200)
2619 ast_copy_string(fn, "digits/100E", sizeof(fn));
2620 else {
2621 if (mf < 0 && num > 199)
2622 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2623 else
2624 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2625 if (num % 100)
2626 playh = 1;
2627 }
2628 num = num % 100;
2629 } else if (num < 1000000) {
2630 if (num > 1999) {
2631 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2632 if (res)
2633 return res;
2634 }
2635 ast_copy_string(fn, "digits/1000", sizeof(fn));
2636 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2637 playh = 1;
2638 num = num % 1000;
2639 } else if (num < 1000000000) {
2640 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2641 if (res)
2642 return res;
2643 if (num < 2000000)
2644 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2645 else
2646 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2647
2648 if ((num % 1000000) &&
2649 /* no thousands */
2650 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2651 /* no hundreds and below */
2652 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2653 playh = 1;
2654 num = num % 1000000;
2655 } else {
2656 /* number is too big */
2657 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2658 res = -1;
2659 }
2660 if (!res) {
2661 if (!ast_streamfile(chan, fn, language)) {
2662 if ((audiofd > -1) && (ctrlfd > -1))
2663 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2664 else
2665 res = ast_waitstream(chan, ints);
2666 }
2667 ast_stopstream(chan);
2668 }
2669 if (!res && playh) {
2670 res = wait_file(chan, ints, "digits/pt-e", language);
2671 ast_stopstream(chan);
2672 playh = 0;
2673 }
2674 }
2675 return res;
2676}
2677
2678/*! \brief ast_say_number_full_se: Swedish syntax
2679
2680 Sound files needed
2681 - 1N
2682*/
2683static 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)
2684{
2685 int playh = 0;
2686 int start = 1;
2687 char fn[256] = "";
2688 int cn = 1; /* +1 = commune; -1 = neuter */
2689 int res = 0;
2690
2691 if (!num) {
2692 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2693 }
2694 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2695
2696 while (num || playh) {
2697 if (num < 0) {
2698 ast_copy_string(fn, "digits/minus", sizeof(fn));
2699 if ( num > INT_MIN ) {
2700 num = -num;
2701 } else {
2702 num = 0;
2703 }
2704 } else if (playh) {
2705 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2706 playh = 0;
2707 } else if (start && num < 200 && num > 99 && cn == -1) {
2708 /* Don't say "en hundra" just say "hundra". */
2709 snprintf(fn, sizeof(fn), "digits/hundred");
2710 num -= 100;
2711 } else if (num == 1 && cn == -1) { /* En eller ett? */
2712 ast_copy_string(fn, "digits/1N", sizeof(fn));
2713 num = 0;
2714 } else if (num < 20) {
2715 snprintf(fn, sizeof(fn), "digits/%d", num);
2716 num = 0;
2717 } else if (num < 100) { /* Below hundreds - teens and tens */
2718 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2719 num %= 10;
2720 } else if (num < 1000) {
2721 /* Hundreds */
2722 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2723 playh++;
2724 num %= 100;
2725 } else if (num < 1000000) { /* 1,000,000 */
2726 /* Always say "ett hundra tusen", not "en hundra tusen" */
2727 res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2728 if (res) {
2729 return res;
2730 }
2731 num %= 1000;
2732 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2733 } else if (num < 1000000000) { /* 1,000,000,000 */
2734 /* Always say "en miljon", not "ett miljon" */
2735 res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2736 if (res) {
2737 return res;
2738 }
2739 num %= 1000000;
2740 ast_copy_string(fn, "digits/million", sizeof(fn));
2741 } else { /* Miljarder - Billions */
2742 ast_debug(1, "Number '%d' is too big for me\n", num);
2743 return -1;
2744 }
2745
2746 if (!ast_streamfile(chan, fn, language)) {
2747 if ((audiofd > -1) && (ctrlfd > -1)) {
2748 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2749 } else {
2750 res = ast_waitstream(chan, ints);
2751 }
2752 ast_stopstream(chan);
2753 if (res) {
2754 return res;
2755 }
2756 }
2757 start = 0;
2758 }
2759 return 0;
2760}
2761
2762/*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2763static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2764{
2765 int res = 0;
2766 int playh = 0;
2767 int playt = 0;
2768 int playz = 0;
2769 int last_length = 0;
2770 char buf[20] = "";
2771 char fn[256] = "";
2772 if (!num)
2773 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2774
2775 while (!res && (num || playh || playt || playz)) {
2776 if (num < 0) {
2777 ast_copy_string(fn, "digits/minus", sizeof(fn));
2778 if ( num > INT_MIN ) {
2779 num = -num;
2780 } else {
2781 num = 0;
2782 }
2783 } else if (playz) {
2784 snprintf(fn, sizeof(fn), "digits/0");
2785 last_length = 0;
2786 playz = 0;
2787 } else if (playh) {
2788 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2789 playh = 0;
2790 } else if (playt) {
2791 snprintf(fn, sizeof(fn), "digits/thousand");
2792 playt = 0;
2793 } else if (num < 10) {
2794 snprintf(buf, 12, "%d", num);
2795 if (last_length - strlen(buf) > 1 && last_length != 0) {
2796 last_length = strlen(buf);
2797 playz++;
2798 continue;
2799 }
2800 snprintf(fn, sizeof(fn), "digits/%d", num);
2801 num = 0;
2802 } else if (num < 100) {
2803 snprintf(buf, 10, "%d", num);
2804 if (last_length - strlen(buf) > 1 && last_length != 0) {
2805 last_length = strlen(buf);
2806 playz++;
2807 continue;
2808 }
2809 last_length = strlen(buf);
2810 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2811 num %= 10;
2812 } else {
2813 if (num < 1000){
2814 snprintf(buf, 10, "%d", num);
2815 if (last_length - strlen(buf) > 1 && last_length != 0) {
2816 last_length = strlen(buf);
2817 playz++;
2818 continue;
2819 }
2820 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2821 playh++;
2822 snprintf(buf, 10, "%d", num);
2823 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2824 last_length = strlen(buf);
2825 num -= ((num / 100) * 100);
2826 } else if (num < 10000){
2827 snprintf(buf, 10, "%d", num);
2828 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2829 playt++;
2830 snprintf(buf, 10, "%d", num);
2831 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2832 last_length = strlen(buf);
2833 num -= ((num / 1000) * 1000);
2834 } else if (num < 100000000) { /* 100,000,000 */
2835 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2836 if (res)
2837 return res;
2838 snprintf(buf, 10, "%d", num);
2839 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2840 num -= ((num / 10000) * 10000);
2841 last_length = strlen(buf);
2842 snprintf(fn, sizeof(fn), "digits/wan");
2843 } else {
2844 if (num < 1000000000) { /* 1,000,000,000 */
2845 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2846 if (res)
2847 return res;
2848 snprintf(buf, 10, "%d", num);
2849 ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2850 last_length = strlen(buf);
2851 num -= ((num / 100000000) * 100000000);
2852 snprintf(fn, sizeof(fn), "digits/yi");
2853 } else {
2854 ast_debug(1, "Number '%d' is too big for me\n", num);
2855 res = -1;
2856 }
2857 }
2858 }
2859 if (!res) {
2860 if (!ast_streamfile(chan, fn, language)) {
2861 if ((audiofd > -1) && (ctrlfd > -1))
2862 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2863 else
2864 res = ast_waitstream(chan, ints);
2865 }
2866 ast_stopstream(chan);
2867 }
2868 }
2869 return res;
2870}
2871
2872/*!\internal
2873 * \brief Counting in Urdu, the national language of Pakistan
2874 * \since 1.8
2875 */
2876static 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)
2877{
2878 int res = 0;
2879 int playh = 0;
2880 char fn[256] = "";
2881
2882 if (!num) {
2883 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2884 }
2885
2886 while (!res && (num || playh)) {
2887 if (playh) {
2888 snprintf(fn, sizeof(fn), "digits/hundred");
2889 playh = 0;
2890 } else if (num < 100) {
2891 snprintf(fn, sizeof(fn), "digits/%d", num);
2892 num = 0;
2893 } else if (num < 1000) {
2894 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2895 playh++;
2896 num -= ((num / 100) * 100);
2897 } else if (num < 100000) { /* 1,00,000 */
2898 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2899 return res;
2900 }
2901 num = num % 1000;
2902 snprintf(fn, sizeof(fn), "digits/thousand");
2903 } else if (num < 10000000) { /* 1,00,00,000 */
2904 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2905 return res;
2906 }
2907 num = num % 100000;
2908 snprintf(fn, sizeof(fn), "digits/lac");
2909 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2910 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2911 return res;
2912 }
2913 num = num % 10000000;
2914 snprintf(fn, sizeof(fn), "digits/crore");
2915 } else {
2916 ast_debug(1, "Number '%d' is too big for me\n", num);
2917 res = -1;
2918 }
2919
2920 if (!res) {
2921 if (!ast_streamfile(chan, fn, language)) {
2922 if ((audiofd > -1) && (ctrlfd > -1)) {
2923 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2924 } else {
2925 res = ast_waitstream(chan, ints);
2926 }
2927 }
2928 ast_stopstream(chan);
2929 }
2930 }
2931 return res;
2932}
2933
2934/*! \brief determine last digits for thousands/millions (ru) */
2935static int get_lastdigits_ru(int num) {
2936 if (num < 20) {
2937 return num;
2938 } else if (num < 100) {
2939 return get_lastdigits_ru(num % 10);
2940 } else if (num < 1000) {
2941 return get_lastdigits_ru(num % 100);
2942 }
2943 return 0; /* number too big */
2944}
2945
2946
2947/*! \brief ast_say_number_full_ru: Russian syntax
2948
2949 additional files:
2950 n00.gsm (one hundred, two hundred, ...)
2951 thousand.gsm
2952 million.gsm
2953 thousands-i.gsm (tisyachi)
2954 million-a.gsm (milliona)
2955 thousands.gsm
2956 millions.gsm
2957 1f.gsm (odna)
2958 2f.gsm (dve)
2959
2960 where 'n' from 1 to 9
2961*/
2962static 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)
2963{
2964 int res = 0;
2965 int lastdigits = 0;
2966 char fn[256] = "";
2967 if (!num)
2968 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2969
2970 while (!res && (num)) {
2971 if (num < 0) {
2972 ast_copy_string(fn, "digits/minus", sizeof(fn));
2973 if ( num > INT_MIN ) {
2974 num = -num;
2975 } else {
2976 num = 0;
2977 }
2978 } else if (num < 20) {
2979 if (options && strlen(options) == 1 && num < 3) {
2980 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2981 } else {
2982 snprintf(fn, sizeof(fn), "digits/%d", num);
2983 }
2984 num = 0;
2985 } else if (num < 100) {
2986 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2987 num %= 10;
2988 } else if (num < 1000){
2989 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2990 num %= 100;
2991 } else if (num < 1000000) { /* 1,000,000 */
2992 lastdigits = get_lastdigits_ru(num / 1000);
2993 /* say thousands */
2994 if (lastdigits < 3) {
2995 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2996 } else {
2997 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2998 }
2999 if (res)
3000 return res;
3001 if (lastdigits == 1) {
3002 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3003 } else if (lastdigits > 1 && lastdigits < 5) {
3004 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
3005 } else {
3006 ast_copy_string(fn, "digits/thousands", sizeof(fn));
3007 }
3008 num %= 1000;
3009 } else if (num < 1000000000) { /* 1,000,000,000 */
3010 lastdigits = get_lastdigits_ru(num / 1000000);
3011 /* say millions */
3012 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
3013 if (res)
3014 return res;
3015 if (lastdigits == 1) {
3016 ast_copy_string(fn, "digits/million", sizeof(fn));
3017 } else if (lastdigits > 1 && lastdigits < 5) {
3018 ast_copy_string(fn, "digits/million-a", sizeof(fn));
3019 } else {
3020 ast_copy_string(fn, "digits/millions", sizeof(fn));
3021 }
3022 num %= 1000000;
3023 } else {
3024 ast_debug(1, "Number '%d' is too big for me\n", num);
3025 res = -1;
3026 }
3027 if (!res) {
3028 if (!ast_streamfile(chan, fn, language)) {
3029 if ((audiofd > -1) && (ctrlfd > -1))
3030 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3031 else
3032 res = ast_waitstream(chan, ints);
3033 }
3034 ast_stopstream(chan);
3035 }
3036 }
3037 return res;
3038}
3039
3040/*! \brief Thai syntax */
3041static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3042{
3043 int res = 0;
3044 int playh = 0;
3045 char fn[256] = "";
3046 if (!num)
3047 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3048
3049 while(!res && (num || playh)) {
3050 if (num < 0) {
3051 ast_copy_string(fn, "digits/lop", sizeof(fn));
3052 if ( num > INT_MIN ) {
3053 num = -num;
3054 } else {
3055 num = 0;
3056 }
3057 } else if (playh) {
3058 ast_copy_string(fn, "digits/roi", sizeof(fn));
3059 playh = 0;
3060 } else if (num < 100) {
3061 if ((num <= 20) || ((num % 10) == 1)) {
3062 snprintf(fn, sizeof(fn), "digits/%d", num);
3063 num = 0;
3064 } else {
3065 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3066 num %= 10;
3067 }
3068 } else if (num < 1000) {
3069 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3070 playh++;
3071 num %= 100;
3072 } else if (num < 10000) { /* 10,000 */
3073 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
3074 if (res)
3075 return res;
3076 num %= 1000;
3077 ast_copy_string(fn, "digits/pan", sizeof(fn));
3078 } else if (num < 100000) { /* 100,000 */
3079 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
3080 if (res)
3081 return res;
3082 num %= 10000;
3083 ast_copy_string(fn, "digits/muan", sizeof(fn));
3084 } else if (num < 1000000) { /* 1,000,000 */
3085 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
3086 if (res)
3087 return res;
3088 num %= 100000;
3089 ast_copy_string(fn, "digits/san", sizeof(fn));
3090 } else {
3091 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3092 if (res)
3093 return res;
3094 num %= 1000000;
3095 ast_copy_string(fn, "digits/larn", sizeof(fn));
3096 }
3097 if (!res) {
3098 if(!ast_streamfile(chan, fn, language)) {
3099 if ((audiofd > -1) && (ctrlfd > -1))
3100 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3101 else
3102 res = ast_waitstream(chan, ints);
3103 }
3104 ast_stopstream(chan);
3105 }
3106 }
3107 return res;
3108}
3109
3110/*! \brief ast_say_number_full_vi: Vietnamese syntax */
3111static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3112{
3113 int res = 0;
3114 int playh = 0;
3115 int playoh = 0;
3116 int playohz = 0;
3117 int playz = 0;
3118 int playl = 0;
3119 char fn[256] = "";
3120 if (!num)
3121 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3122 while (!res && (num || playh)) {
3123 if (num < 0) {
3124 ast_copy_string(fn, "digits/minus", sizeof(fn));
3125 if ( num > INT_MIN ) {
3126 num = -num;
3127 } else {
3128 num = 0;
3129 }
3130 } else if (playl) {
3131 snprintf(fn, sizeof(fn), "digits/%da", num);
3132 playl = 0;
3133 num = 0;
3134 } else if (playh) {
3135 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3136 playh = 0;
3137 } else if (playz) {
3138 ast_copy_string(fn, "digits/odd", sizeof(fn));
3139 playz = 0;
3140 } else if (playoh) {
3141 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
3142 playoh = 0;
3143 } else if (playohz) {
3144 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
3145 playohz = 0;
3146 } else if (num < 20) {
3147 snprintf(fn, sizeof(fn), "digits/%d", num);
3148 num = 0;
3149 } else if (num < 100) {
3150 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
3151 num %= 10;
3152 if ((num == 5) || (num == 4) || (num == 1)) playl++;
3153 } else {
3154 if (num < 1000) {
3155 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3156 num %= 100;
3157 if (num && (num < 10)) {
3158 playz++;
3159 playh++;
3160 } else {
3161 playh++;
3162 }
3163 } else {
3164 if (num < 1000000) { /* 1,000,000 */
3165 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
3166 if (res)
3167 return res;
3168 num %= 1000;
3169 snprintf(fn, sizeof(fn), "digits/thousand");
3170 if (num && (num < 10)) {
3171 playohz++;
3172 } else if (num && (num < 100)){
3173 playoh++;
3174 } else {
3175 playh = 0;
3176 playohz = 0;
3177 playoh = 0;
3178 }
3179 } else {
3180 if (num < 1000000000) { /* 1,000,000,000 */
3181 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3182 if (res)
3183 return res;
3184 num %= 1000000;
3185 ast_copy_string(fn, "digits/million", sizeof(fn));
3186 } else {
3187 res = -1;
3188 }
3189 }
3190 }
3191 }
3192 if (!res) {
3193 if (!ast_streamfile(chan, fn, language)) {
3194 if ((audiofd > -1) && (ctrlfd > -1))
3195 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3196 else
3197 res = ast_waitstream(chan, ints);
3198 }
3199 ast_stopstream(chan);
3200 }
3201 }
3202 return res;
3203}
3204
3205/*! \brief ast_say_enumeration_full: call language-specific functions
3206 * \note Called from AGI */
3207static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3208{
3209 if (!strncasecmp(language, "en", 2)) { /* English syntax */
3210 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3211 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
3212 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
3213 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
3214 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
3215 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
3216 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
3217 } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
3218 return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
3219 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
3220 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3221 }
3222
3223 /* Default to english */
3224 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3225}
3226
3227/*! \brief ast_say_enumeration_full_en: English syntax
3228 \note This is the default syntax, if no other syntax defined in this file is used */
3229static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3230{
3231 int res = 0, t = 0;
3232 char fn[256] = "";
3233
3234 while (!res && num) {
3235 if (num < 0) {
3236 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3237 if ( num > INT_MIN ) {
3238 num = -num;
3239 } else {
3240 num = 0;
3241 }
3242 } else if (num < 20) {
3243 snprintf(fn, sizeof(fn), "digits/h-%d", num);
3244 num = 0;
3245 } else if (num < 100) {
3246 int tens = num / 10;
3247 num = num % 10;
3248 if (num == 0) {
3249 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3250 } else {
3251 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3252 }
3253 } else if (num < 1000) {
3254 int hundreds = num / 100;
3255 num = num % 100;
3256 if (hundreds > 1 || t == 1) {
3257 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3258 }
3259 if (res)
3260 return res;
3261 if (num) {
3262 ast_copy_string(fn, "digits/hundred", sizeof(fn));
3263 } else {
3264 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3265 }
3266 } else if (num < 1000000) {
3267 int thousands = num / 1000;
3268 num = num % 1000;
3269 if (thousands > 1 || t == 1) {
3270 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3271 }
3272 if (res)
3273 return res;
3274 if (num) {
3275 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3276 } else {
3277 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3278 }
3279 t = 1;
3280 } else if (num < 1000000000) {
3281 int millions = num / 1000000;
3282 num = num % 1000000;
3283 t = 1;
3284 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3285 if (res)
3286 return res;
3287 if (num) {
3288 ast_copy_string(fn, "digits/million", sizeof(fn));
3289 } else {
3290 ast_copy_string(fn, "digits/h-million", sizeof(fn));
3291 }
3292 } else if (num < INT_MAX) {
3293 int billions = num / 1000000000;
3294 num = num % 1000000000;
3295 t = 1;
3296 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3297 if (res)
3298 return res;
3299 if (num) {
3300 ast_copy_string(fn, "digits/billion", sizeof(fn));
3301 } else {
3302 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3303 }
3304 } else if (num == INT_MAX) {
3305 ast_copy_string(fn, "digits/h-last", sizeof(fn));
3306 num = 0;
3307 } else {
3308 ast_debug(1, "Number '%d' is too big for me\n", num);
3309 res = -1;
3310 }
3311
3312 if (!res) {
3313 if (!ast_streamfile(chan, fn, language)) {
3314 if ((audiofd > -1) && (ctrlfd > -1)) {
3315 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3316 } else {
3317 res = ast_waitstream(chan, ints);
3318 }
3319 }
3320 ast_stopstream(chan);
3321 }
3322 }
3323 return res;
3324}
3325
3326static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3327{
3328 int res = 0;
3329 char fn[256] = "";
3330 ast_copy_string(fn, "digits/h", sizeof(fn));
3331 if (!res) {
3332 if (!ast_streamfile(chan, fn, language)) {
3333 if ((audiofd > -1) && (ctrlfd > -1)) {
3334 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3335 } else {
3336 res = ast_waitstream(chan, ints);
3337 }
3338 }
3339 ast_stopstream(chan);
3340 }
3341
3342 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3343}
3344
3345/*! \brief ast_say_enumeration_full_da: Danish syntax */
3346static 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)
3347{
3348 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3349 int res = 0, t = 0;
3350 char fn[256] = "", fna[256] = "";
3351 char *gender;
3352
3353 if (options && !strncasecmp(options, "f", 1)) {
3354 gender = "F";
3355 } else if (options && !strncasecmp(options, "n", 1)) {
3356 gender = "N";
3357 } else {
3358 gender = "";
3359 }
3360
3361 if (!num)
3362 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3363
3364 while (!res && num) {
3365 if (num < 0) {
3366 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3367 if ( num > INT_MIN ) {
3368 num = -num;
3369 } else {
3370 num = 0;
3371 }
3372 } else if (num < 100 && t) {
3373 ast_copy_string(fn, "digits/and", sizeof(fn));
3374 t = 0;
3375 } else if (num < 20) {
3376 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3377 num = 0;
3378 } else if (num < 100) {
3379 int ones = num % 10;
3380 if (ones) {
3381 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3382 num -= ones;
3383 } else {
3384 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3385 num = 0;
3386 }
3387 } else if (num == 100 && t == 0) {
3388 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3389 num = 0;
3390 } else if (num < 1000) {
3391 int hundreds = num / 100;
3392 num = num % 100;
3393 if (hundreds == 1) {
3394 ast_copy_string(fn, "digits/1N", sizeof(fn));
3395 } else {
3396 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3397 }
3398 if (num) {
3399 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3400 } else {
3401 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3402 }
3403 t = 1;
3404 } else if (num < 1000000) {
3405 int thousands = num / 1000;
3406 num = num % 1000;
3407 if (thousands == 1) {
3408 if (num) {
3409 ast_copy_string(fn, "digits/1N", sizeof(fn));
3410 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3411 } else {
3412 if (t) {
3413 ast_copy_string(fn, "digits/1N", sizeof(fn));
3414 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3415 } else {
3416 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3417 }
3418 }
3419 } else {
3420 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3421 if (res) {
3422 return res;
3423 }
3424 if (num) {
3425 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3426 } else {
3427 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3428 }
3429 }
3430 t = 1;
3431 } else if (num < 1000000000) {
3432 int millions = num / 1000000;
3433 num = num % 1000000;
3434 if (millions == 1) {
3435 if (num) {
3436 ast_copy_string(fn, "digits/1F", sizeof(fn));
3437 ast_copy_string(fna, "digits/million", sizeof(fna));
3438 } else {
3439 ast_copy_string(fn, "digits/1N", sizeof(fn));
3440 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3441 }
3442 } else {
3443 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3444 if (res) {
3445 return res;
3446 }
3447 if (num) {
3448 ast_copy_string(fn, "digits/millions", sizeof(fn));
3449 } else {
3450 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3451 }
3452 }
3453 t = 1;
3454 } else if (num < INT_MAX) {
3455 int billions = num / 1000000000;
3456 num = num % 1000000000;
3457 if (billions == 1) {
3458 if (num) {
3459 ast_copy_string(fn, "digits/1F", sizeof(fn));
3460 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3461 } else {
3462 ast_copy_string(fn, "digits/1N", sizeof(fn));
3463 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3464 }
3465 } else {
3466 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3467 if (res)
3468 return res;
3469 if (num) {
3470 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3471 } else {
3472 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3473 }
3474 }
3475 t = 1;
3476 } else if (num == INT_MAX) {
3477 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3478 num = 0;
3479 } else {
3480 ast_debug(1, "Number '%d' is too big for me\n", num);
3481 res = -1;
3482 }
3483
3484 if (!res) {
3485 if (!ast_streamfile(chan, fn, language)) {
3486 if ((audiofd > -1) && (ctrlfd > -1))
3487 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3488 else
3489 res = ast_waitstream(chan, ints);
3490 }
3491 ast_stopstream(chan);
3492 if (!res) {
3493 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3494 if ((audiofd > -1) && (ctrlfd > -1)) {
3495 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3496 } else {
3497 res = ast_waitstream(chan, ints);
3498 }
3499 }
3500 ast_stopstream(chan);
3501 strcpy(fna, "");
3502 }
3503 }
3504 }
3505 return res;
3506}
3507
3508/*! \brief ast_say_enumeration_full_de: German syntax */
3509static 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)
3510{
3511 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3512 int res = 0, t = 0;
3513 char fn[256] = "", fna[256] = "";
3514 char *gender;
3515
3516 if (options && !strncasecmp(options, "f", 1)) {
3517 gender = "F";
3518 } else if (options && !strncasecmp(options, "n", 1)) {
3519 gender = "N";
3520 } else {
3521 gender = "";
3522 }
3523
3524 if (!num)
3525 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3526
3527 while (!res && num) {
3528 if (num < 0) {
3529 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3530 if ( num > INT_MIN ) {
3531 num = -num;
3532 } else {
3533 num = 0;
3534 }
3535 } else if (num < 100 && t) {
3536 ast_copy_string(fn, "digits/and", sizeof(fn));
3537 t = 0;
3538 } else if (num < 20) {
3539 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3540 num = 0;
3541 } else if (num < 100) {
3542 int ones = num % 10;
3543 if (ones) {
3544 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3545 num -= ones;
3546 } else {
3547 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3548 num = 0;
3549 }
3550 } else if (num == 100 && t == 0) {
3551 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3552 num = 0;
3553 } else if (num < 1000) {
3554 int hundreds = num / 100;
3555 num = num % 100;
3556 if (hundreds == 1) {
3557 ast_copy_string(fn, "digits/1N", sizeof(fn));
3558 } else {
3559 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3560 }
3561 if (num) {
3562 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3563 } else {
3564 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3565 }
3566 t = 1;
3567 } else if (num < 1000000) {
3568 int thousands = num / 1000;
3569 num = num % 1000;
3570 if (thousands == 1) {
3571 if (num) {
3572 ast_copy_string(fn, "digits/1N", sizeof(fn));
3573 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3574 } else {
3575 if (t) {
3576 ast_copy_string(fn, "digits/1N", sizeof(fn));
3577 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3578 } else {
3579 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3580 }
3581 }
3582 } else {
3583 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3584 if (res) {
3585 return res;
3586 }
3587 if (num) {
3588 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3589 } else {
3590 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3591 }
3592 }
3593 t = 1;
3594 } else if (num < 1000000000) {
3595 int millions = num / 1000000;
3596 num = num % 1000000;
3597 if (millions == 1) {
3598 if (num) {
3599 ast_copy_string(fn, "digits/1F", sizeof(fn));
3600 ast_copy_string(fna, "digits/million", sizeof(fna));
3601 } else {
3602 ast_copy_string(fn, "digits/1N", sizeof(fn));
3603 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3604 }
3605 } else {
3606 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3607 if (res) {
3608 return res;
3609 }
3610 if (num) {
3611 ast_copy_string(fn, "digits/millions", sizeof(fn));
3612 } else {
3613 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3614 }
3615 }
3616 t = 1;
3617 } else if (num < INT_MAX) {
3618 int billions = num / 1000000000;
3619 num = num % 1000000000;
3620 if (billions == 1) {
3621 if (num) {
3622 ast_copy_string(fn, "digits/1F", sizeof(fn));
3623 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3624 } else {
3625 ast_copy_string(fn, "digits/1N", sizeof(fn));
3626 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3627 }
3628 } else {
3629 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3630 if (res)
3631 return res;
3632 if (num) {
3633 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3634 } else {
3635 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3636 }
3637 }
3638 t = 1;
3639 } else if (num == INT_MAX) {
3640 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3641 num = 0;
3642 } else {
3643 ast_debug(1, "Number '%d' is too big for me\n", num);
3644 res = -1;
3645 }
3646
3647 if (!res) {
3648 if (!ast_streamfile(chan, fn, language)) {
3649 if ((audiofd > -1) && (ctrlfd > -1))
3650 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3651 else
3652 res = ast_waitstream(chan, ints);
3653 }
3654 ast_stopstream(chan);
3655 if (!res) {
3656 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3657 if ((audiofd > -1) && (ctrlfd > -1)) {
3658 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3659 } else {
3660 res = ast_waitstream(chan, ints);
3661 }
3662 }
3663 ast_stopstream(chan);
3664 strcpy(fna, "");
3665 }
3666 }
3667 }
3668 return res;
3669}
3670
3671static 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)
3672{
3673 int res = 0;
3674 char fn[256] = "";
3675 int mf = -1; /* +1 = Masculin; -1 = Feminin */
3676 ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
3677
3678 if (options && !strncasecmp(options, "m", 1)) {
3679 mf = -1;
3680 }
3681
3682 ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
3683
3684 while (!res && num) {
3685 if (num < 0) {
3686 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
3687 if (num > INT_MIN) {
3688 num = -num;
3689 } else {
3690 num = 0;
3691 }
3692 } else if (num < 21) {
3693 if (mf < 0) {
3694 if (num < 10) {
3695 snprintf(fn, sizeof(fn), "digits/f-0%d", num);
3696 } else {
3697 snprintf(fn, sizeof(fn), "digits/f-%d", num);
3698 }
3699 } else {
3700 if (num < 10) {
3701 snprintf(fn, sizeof(fn), "digits/m-0%d", num);
3702 } else {
3703 snprintf(fn, sizeof(fn), "digits/m-%d", num);
3704 }
3705 }
3706 num = 0;
3707 } else if ((num < 100) && num >= 20) {
3708 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3709 num = num % 10;
3710 } else if ((num >= 100) && (num < 1000)) {
3711 int tmpnum = num / 100;
3712 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
3713 num = num - (tmpnum * 100);
3714 } else if ((num >= 1000) && (num < 10000)) {
3715 int tmpnum = num / 1000;
3716 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
3717 num = num - (tmpnum * 1000);
3718 } else if (num < 20000) {
3719 snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
3720 num = num % 1000;
3721 } else if (num < 1000000) {
3722 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
3723 if (res) {
3724 return res;
3725 }
3726 snprintf(fn, sizeof(fn), "digits/1k");
3727 num = num % 1000;
3728 } else if (num < 2000000) {
3729 snprintf(fn, sizeof(fn), "digits/1m");
3730 num = num % 1000000;
3731 } else if (num < 3000000) {
3732 snprintf(fn, sizeof(fn), "digits/2m");
3733 num = num - 2000000;
3734 } else if (num < 1000000000) {
3735 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
3736 if (res) {
3737 return res;
3738 }
3739 snprintf(fn, sizeof(fn), "digits/1m");
3740 num = num % 1000000;
3741 } else {
3742 ast_debug(1, "Number '%d' is too big for me\n", num);
3743 res = -1;
3744 }
3745 if (!res) {
3746 if (!ast_streamfile(chan, fn, language)) {
3747 if ((audiofd > -1) && (ctrlfd > -1)) {
3748 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3749 } else {
3750 res = ast_waitstream(chan, ints);
3751 }
3752 }
3753 ast_stopstream(chan);
3754 }
3755 }
3756 return res;
3757}
3758
3759/*! \brief ast_say_enumeration_full_is: Icelandic syntax */
3760static 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)
3761{
3762 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3763 int res = 0, t = 0;
3764 char fn[256] = "", fna[256] = "";
3765 char *gender;
3766
3767 if (options && !strncasecmp(options, "f", 1)) {
3768 gender = "F";
3769 } else if (options && !strncasecmp(options, "n", 1)) {
3770 gender = "N";
3771 } else {
3772 gender = "";
3773 }
3774
3775 if (!num)
3776 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3777
3778 while (!res && num) {
3779 if (num < 0) {
3780 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3781 if ( num > INT_MIN ) {
3782 num = -num;
3783 } else {
3784 num = 0;
3785 }
3786 } else if (num < 100 && t) {
3787 ast_copy_string(fn, "digits/and", sizeof(fn));
3788 t = 0;
3789 } else if (num < 20) {
3790 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3791 num = 0;
3792 } else if (num < 100) {
3793 int ones = num % 10;
3794 if (ones) {
3795 int tens = num - ones;
3796 snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender);
3797 num = ones;
3798 t++;
3799 }
3800 else if (t) {
3801 snprintf(fn, sizeof(fn), "digits/and");
3802 t = 0;
3803 }
3804 else {
3805 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3806 num = 0;
3807 }
3808
3809 } else if (num == 100 && t == 0) {
3810 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3811 num = 0;
3812 } else if (num < 1000) {
3813 int hundreds = num / 100;
3814 num = num % 100;
3815 if (hundreds == 1) {
3816 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3817 } else {
3818 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3819 }
3820 if (num) {
3821 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3822 } else {
3823 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3824 }
3825 t = 1;
3826 } else if (num < 1000000) {
3827 int thousands = num / 1000;
3828 num = num % 1000;
3829 if (thousands == 1) {
3830 if (num) {
3831 /* Thousand is a neutral word, so use the neutral recording */
3832 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3833 ast_copy_string(fna, "digits/thousand", sizeof(fna));
3834 } else {
3835 if (t) {
3836 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3837 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3838 } else {
3839 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3840 }
3841 }
3842 } else {
3843 res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd);
3844 if (res) {
3845 return res;
3846 }
3847 if (num) {
3848 ast_copy_string(fn, "digits/thousand", sizeof(fn));
3849 } else {
3850 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3851 }
3852 }
3853 if (num)
3854 t = 1;
3855 } else if (num < 1000000000) {
3856 int millions = num / 1000000;
3857 num = num % 1000000;
3858 if (millions == 1) {
3859 if (num) {
3860 /* Million is a feminine word, so use the female form */
3861 ast_copy_string(fn, "digits/1kvk", sizeof(fn));
3862 ast_copy_string(fna, "digits/million", sizeof(fna));
3863 } else {
3864 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3865 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3866 }
3867 } else {
3868 res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd);
3869 if (res) {
3870 return res;
3871 }
3872 if (num) {
3873 ast_copy_string(fn, "digits/millions", sizeof(fn));
3874 } else {
3875 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3876 }
3877 }
3878 if (num)
3879 t = 1;
3880 } else if (num < INT_MAX) {
3881 int billions = num / 1000000000;
3882 num = num % 1000000000;
3883 if (billions == 1) {
3884 if (num) {
3885 ast_copy_string(fn, "digits/1", sizeof(fn));
3886 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3887 } else {
3888 ast_copy_string(fn, "digits/1hk", sizeof(fn));
3889 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3890 }
3891 } else {
3892 res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd);
3893 if (res)
3894 return res;
3895 if (num) {
3896 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3897 } else {
3898 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3899 }
3900 }
3901 if (num)
3902 t = 1;
3903 } else if (num == INT_MAX) {
3904 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3905 num = 0;
3906 } else {
3907 ast_debug(1, "Number '%d' is too big for me\n", num);
3908 res = -1;
3909 }
3910
3911 if (!res) {
3912 if (!ast_streamfile(chan, fn, language)) {
3913 if ((audiofd > -1) && (ctrlfd > -1))
3914 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3915 else
3916 res = ast_waitstream(chan, ints);
3917 }
3918 ast_stopstream(chan);
3919 if (!res) {
3920 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3921 if ((audiofd > -1) && (ctrlfd > -1)) {
3922 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3923 } else {
3924 res = ast_waitstream(chan, ints);
3925 }
3926 }
3927 ast_stopstream(chan);
3928 strcpy(fna, "");
3929 }
3930 }
3931 }
3932 return res;
3933}
3934
3935static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3936{
3937 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
3938 return ast_say_date_en(chan, t, ints, lang);
3939 } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
3940 return ast_say_date_da(chan, t, ints, lang);
3941 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
3942 return ast_say_date_de(chan, t, ints, lang);
3943 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
3944 return ast_say_date_fr(chan, t, ints, lang);
3945 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
3946 return ast_say_date_gr(chan, t, ints, lang);
3947 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
3948 return ast_say_date_ja(chan, t, ints, lang);
3949 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
3950 return ast_say_date_he(chan, t, ints, lang);
3951 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
3952 return ast_say_date_hu(chan, t, ints, lang);
3953 } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
3954 return ast_say_date_is(chan, t, ints, lang);
3955 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
3956 return ast_say_date_ka(chan, t, ints, lang);
3957 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
3958 return ast_say_date_nl(chan, t, ints, lang);
3959 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
3960 return ast_say_date_pt(chan, t, ints, lang);
3961 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
3962 return ast_say_date_th(chan, t, ints, lang);
3963 }
3964
3965 /* Default to English */
3966 return ast_say_date_en(chan, t, ints, lang);
3967}
3968
3969/*! \brief English syntax */
3970int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3971{
3972 struct ast_tm tm;
3973 struct timeval when = { t, 0 };
3974 char fn[256];
3975 int res = 0;
3976 ast_localtime(&when, &tm, NULL);
3977 if (!res) {
3978 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3979 res = ast_streamfile(chan, fn, lang);
3980 if (!res)
3981 res = ast_waitstream(chan, ints);
3982 }
3983 if (!res) {
3984 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3985 res = ast_streamfile(chan, fn, lang);
3986 if (!res)
3987 res = ast_waitstream(chan, ints);
3988 }
3989 if (!res)
3990 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3991 if (!res)
3992 res = ast_waitstream(chan, ints);
3993 if (!res)
3994 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3995 return res;
3996}
3997
3998/*! \brief Danish syntax */
3999int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4000{
4001 struct timeval when = { t, 0 };
4002 struct ast_tm tm;
4003 char fn[256];
4004 int res = 0;
4005 ast_localtime(&when, &tm, NULL);
4006 if (!res) {
4007 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4008 res = ast_streamfile(chan, fn, lang);
4009 if (!res)
4010 res = ast_waitstream(chan, ints);
4011 }
4012 if (!res)
4013 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4014 if (!res)
4015 res = ast_waitstream(chan, ints);
4016 if (!res) {
4017 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4018 res = ast_streamfile(chan, fn, lang);
4019 if (!res)
4020 res = ast_waitstream(chan, ints);
4021 }
4022 if (!res) {
4023 /* Year */
4024 int year = tm.tm_year + 1900;
4025 if (year > 1999) { /* year 2000 and later */
4026 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4027 } else {
4028 if (year < 1100) {
4029 /* I'm not going to handle 1100 and prior */
4030 /* We'll just be silent on the year, instead of bombing out. */
4031 } else {
4032 /* year 1100 to 1999. will anybody need this?!? */
4033 snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4034 res = wait_file(chan, ints, fn, lang);
4035 if (!res) {
4036 res = wait_file(chan, ints, "digits/hundred", lang);
4037 if (!res && year % 100 != 0) {
4038 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4039 }
4040 }
4041 }
4042 }
4043 }
4044 return res;
4045}
4046
4047/*! \brief German syntax */
4048int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4049{
4050 struct timeval when = { t, 0 };
4051 struct ast_tm tm;
4052 char fn[256];
4053 int res = 0;
4054 ast_localtime(&when, &tm, NULL);
4055 if (!res) {
4056 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4057 res = ast_streamfile(chan, fn, lang);
4058 if (!res)
4059 res = ast_waitstream(chan, ints);
4060 }
4061 if (!res)
4062 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4063 if (!res)
4064 res = ast_waitstream(chan, ints);
4065 if (!res) {
4066 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4067 res = ast_streamfile(chan, fn, lang);
4068 if (!res)
4069 res = ast_waitstream(chan, ints);
4070 }
4071 if (!res) {
4072 /* Year */
4073 int year = tm.tm_year + 1900;
4074 if (year > 1999) { /* year 2000 and later */
4075 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4076 } else {
4077 if (year < 1100) {
4078 /* I'm not going to handle 1100 and prior */
4079 /* We'll just be silent on the year, instead of bombing out. */
4080 } else {
4081 /* year 1100 to 1999. will anybody need this?!? */
4082 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4083 snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
4084 res = wait_file(chan, ints, fn, lang);
4085 if (!res) {
4086 res = wait_file(chan, ints, "digits/hundred", lang);
4087 if (!res && year % 100 != 0) {
4088 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4089 }
4090 }
4091 }
4092 }
4093 }
4094 return res;
4095}
4096
4097/*! \brief Hungarian syntax */
4098int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4099{
4100 struct timeval when = { t, 0 };
4101 struct ast_tm tm;
4102 char fn[256];
4103 int res = 0;
4104 ast_localtime(&when, &tm, NULL);
4105
4106 if (!res)
4107 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4108 if (!res)
4109 res = ast_waitstream(chan, ints);
4110 if (!res) {
4111 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4112 res = ast_streamfile(chan, fn, lang);
4113 if (!res)
4114 res = ast_waitstream(chan, ints);
4115 }
4116 if (!res)
4117 ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
4118 if (!res)
4119 res = ast_waitstream(chan, ints);
4120 if (!res) {
4121 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4122 res = ast_streamfile(chan, fn, lang);
4123 if (!res)
4124 res = ast_waitstream(chan, ints);
4125 }
4126 return res;
4127}
4128
4129/*! \brief French syntax */
4130int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4131{
4132 struct timeval when = { t, 0 };
4133 struct ast_tm tm;
4134 char fn[256];
4135 int res = 0;
4136 ast_localtime(&when, &tm, NULL);
4137 if (!res) {
4138 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4139 res = ast_streamfile(chan, fn, lang);
4140 if (!res)
4141 res = ast_waitstream(chan, ints);
4142 }
4143 if (!res)
4144 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4145 if (!res)
4146 res = ast_waitstream(chan, ints);
4147 if (!res) {
4148 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4149 res = ast_streamfile(chan, fn, lang);
4150 if (!res)
4151 res = ast_waitstream(chan, ints);
4152 }
4153 if (!res)
4154 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4155 return res;
4156}
4157
4158/*! \brief Dutch syntax */
4159int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4160{
4161 struct timeval when = { t, 0 };
4162 struct ast_tm tm;
4163 char fn[256];
4164 int res = 0;
4165 ast_localtime(&when, &tm, NULL);
4166 if (!res) {
4167 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4168 res = ast_streamfile(chan, fn, lang);
4169 if (!res)
4170 res = ast_waitstream(chan, ints);
4171 }
4172 if (!res)
4173 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4174 if (!res) {
4175 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4176 res = ast_streamfile(chan, fn, lang);
4177 if (!res)
4178 res = ast_waitstream(chan, ints);
4179 }
4180 if (!res)
4181 res = ast_waitstream(chan, ints);
4182 if (!res)
4183 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4184 return res;
4185}
4186
4187/*! \brief Thai syntax */
4188int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4189{
4190 struct timeval when = { t, 0 };
4191 struct ast_tm tm;
4192 char fn[256];
4193 int res = 0;
4194 ast_localtime(&when, &tm, NULL);
4195 if (!res) {
4196 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4197 res = ast_streamfile(chan, fn, lang);
4198 ast_copy_string(fn, "digits/tee", sizeof(fn));
4199 res = ast_streamfile(chan, fn, lang);
4200 if (!res)
4201 res = ast_waitstream(chan, ints);
4202 }
4203 if (!res)
4204 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4205 if (!res)
4206 res = ast_waitstream(chan, ints);
4207 if (!res) {
4208 ast_copy_string(fn, "digits/duan", sizeof(fn));
4209 res = ast_streamfile(chan, fn, lang);
4210 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4211 res = ast_streamfile(chan, fn, lang);
4212 if (!res)
4213 res = ast_waitstream(chan, ints);
4214 }
4215 if (!res){
4216 ast_copy_string(fn, "digits/posor", sizeof(fn));
4217 res = ast_streamfile(chan, fn, lang);
4218 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4219 }
4220 return res;
4221}
4222
4223/*! \brief Portuguese syntax */
4224int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4225{
4226 struct timeval when = { t, 0 };
4227 struct ast_tm tm;
4228 char fn[256];
4229 int res = 0;
4230
4231 ast_localtime(&when, &tm, NULL);
4232 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4233 if (!res)
4234 res = wait_file(chan, ints, fn, lang);
4235 if (!res)
4236 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4237 if (!res)
4238 res = wait_file(chan, ints, "digits/pt-de", lang);
4239 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4240 if (!res)
4241 res = wait_file(chan, ints, fn, lang);
4242 if (!res)
4243 res = wait_file(chan, ints, "digits/pt-de", lang);
4244 if (!res)
4245 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4246
4247 return res;
4248}
4249
4250/*! \brief Hebrew syntax */
4251int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4252{
4253 struct timeval when = { t, 0 };
4254 struct ast_tm tm;
4255 char fn[256];
4256 int res = 0;
4257 ast_localtime(&when, &tm, NULL);
4258 if (!res) {
4259 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4260 res = ast_streamfile(chan, fn, lang);
4261 if (!res) {
4262 res = ast_waitstream(chan, ints);
4263 }
4264 }
4265 if (!res) {
4266 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4267 res = ast_streamfile(chan, fn, lang);
4268 if (!res) {
4269 res = ast_waitstream(chan, ints);
4270 }
4271 }
4272 if (!res) {
4273 res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
4274 }
4275 if (!res) {
4276 res = ast_waitstream(chan, ints);
4277 }
4278 if (!res) {
4279 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
4280 }
4281 return res;
4282}
4283
4284/* Icelandic syntax */
4285int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4286{
4287 struct timeval when = { t, 0 };
4288 struct ast_tm tm;
4289 char fn[256];
4290 int res = 0;
4291 ast_localtime(&when, &tm, NULL);
4292 if (!res) {
4293 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4294 res = ast_streamfile(chan, fn, lang);
4295 if (!res)
4296 res = ast_waitstream(chan, ints);
4297 }
4298 if (!res)
4299 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4300 if (!res)
4301 res = ast_waitstream(chan, ints);
4302 if (!res) {
4303 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4304 res = ast_streamfile(chan, fn, lang);
4305 if (!res)
4306 res = ast_waitstream(chan, ints);
4307 }
4308 if (!res) {
4309 /* Year */
4310 int year = tm.tm_year + 1900;
4311 if (year > 1999) { /* year 2000 and later */
4312 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4313 } else {
4314 if (year < 1100) {
4315 /* I'm not going to handle 1100 and prior */
4316 /* We'll just be silent on the year, instead of bombing out. */
4317 } else {
4318 /* year 1100 to 1999. will anybody need this?!? */
4319 snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4320 res = wait_file(chan, ints, fn, lang);
4321 if (!res) {
4322 res = wait_file(chan, ints, "digits/hundred", lang);
4323 if (!res && year % 100 != 0) {
4324 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4325 }
4326 }
4327 }
4328 }
4329 }
4330 return res;
4331}
4332
4333static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4334{
4335 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4336 return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4337 } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4338 return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
4339 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4340 return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
4341 } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
4342 return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
4343 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4344 return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
4345 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4346 return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
4347 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4348 return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
4349 } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4350 return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone);
4351 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4352 return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone);
4353 } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
4354 return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
4355 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4356 return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
4357 } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
4358 return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
4359 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4360 return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
4361 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4362 return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
4363 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
4364 return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
4365 } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
4366 return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
4367 }
4368
4369 /* Default to English */
4370 return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4371}
4372
4373/*! \brief English syntax */
4374int 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)
4375{
4376 struct timeval when = { t, 0 };
4377 struct ast_tm tm;
4378 int res=0, offset, sndoffset;
4379 char sndfile[256], nextmsg[256];
4380
4381 if (format == NULL)
4382 format = "ABdY 'digits/at' IMp";
4383
4384 ast_localtime(&when, &tm, tzone);
4385
4386 for (offset=0 ; format[offset] != '\0' ; offset++) {
4387 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4388 switch (format[offset]) {
4389 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4390 case '\'':
4391 /* Literal name of a sound file */
4392 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4393 sndfile[sndoffset] = format[offset];
4394 }
4395 sndfile[sndoffset] = '\0';
4396 res = wait_file(chan, ints, sndfile, lang);
4397 break;
4398 case 'A':
4399 case 'a':
4400 /* Sunday - Saturday */
4401 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4402 res = wait_file(chan, ints, nextmsg, lang);
4403 break;
4404 case 'B':
4405 case 'b':
4406 case 'h':
4407 /* January - December */
4408 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4409 res = wait_file(chan, ints, nextmsg, lang);
4410 break;
4411 case 'm':
4412 /* Month enumerated */
4413 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
4414 break;
4415 case 'd':
4416 case 'e':
4417 /* First - Thirtyfirst */
4418 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
4419 break;
4420 case 'Y':
4421 /* Year */
4422 if (tm.tm_year > 99) {
4423 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4424 } else if (tm.tm_year < 1) {
4425 /* I'm not going to handle 1900 and prior */
4426 /* We'll just be silent on the year, instead of bombing out. */
4427 } else {
4428 res = wait_file(chan, ints, "digits/19", lang);
4429 if (!res) {
4430 if (tm.tm_year <= 9) {
4431 /* 1901 - 1909 */
4432 res = wait_file(chan, ints, "digits/oh", lang);
4433 }
4434
4435 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
4436 }
4437 }
4438 break;
4439 case 'I':
4440 case 'l':
4441 /* 12-Hour */
4442 if (tm.tm_hour == 0)
4443 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4444 else if (tm.tm_hour > 12)
4445 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4446 else
4447 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4448 res = wait_file(chan, ints, nextmsg, lang);
4449 break;
4450 case 'H':
4451 case 'k':
4452 /* 24-Hour */
4453 if (format[offset] == 'H') {
4454 /* e.g. oh-eight */
4455 if (tm.tm_hour < 10) {
4456 res = wait_file(chan, ints, "digits/oh", lang);
4457 }
4458 } else {
4459 /* e.g. eight */
4460 if (tm.tm_hour == 0) {
4461 res = wait_file(chan, ints, "digits/oh", lang);
4462 }
4463 }
4464 if (!res) {
4465 if (tm.tm_hour != 0) {
4466 int remaining = tm.tm_hour;
4467 if (tm.tm_hour > 20) {
4468 res = wait_file(chan, ints, "digits/20", lang);
4469 remaining -= 20;
4470 }
4471 if (!res) {
4472 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
4473 res = wait_file(chan, ints, nextmsg, lang);
4474 }
4475 }
4476 }
4477 break;
4478 case 'M':
4479 case 'N':
4480 /* Minute */
4481 if (tm.tm_min == 0) {
4482 if (format[offset] == 'M') {
4483 res = wait_file(chan, ints, "digits/oclock", lang);
4484 } else {
4485 res = wait_file(chan, ints, "digits/hundred", lang);
4486 }
4487 } else if (tm.tm_min < 10) {
4488 res = wait_file(chan, ints, "digits/oh", lang);
4489 if (!res) {
4490 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
4491 res = wait_file(chan, ints, nextmsg, lang);
4492 }
4493 } else {
4494 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4495 }
4496 break;
4497 case 'P':
4498 case 'p':
4499 /* AM/PM */
4500 if (tm.tm_hour > 11)
4501 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4502 else
4503 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4504 res = wait_file(chan, ints, nextmsg, lang);
4505 break;
4506 case 'Q':
4507 /* Shorthand for "Today", "Yesterday", or ABdY */
4508 /* XXX As emphasized elsewhere, this should the native way in your
4509 * language to say the date, with changes in what you say, depending
4510 * upon how recent the date is. XXX */
4511 {
4512 struct timeval now = ast_tvnow();
4513 struct ast_tm tmnow;
4514 time_t beg_today;
4515
4516 gettimeofday(&now, NULL);
4517 ast_localtime(&now, &tmnow, tzone);
4518 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4519 /* In any case, it saves not having to do ast_mktime() */
4520 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4521 if (beg_today + 15768000 < t) {
4522 /* More than 6 months from now - "April nineteenth two thousand three" */
4523 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4524 } else if (beg_today + 2628000 < t) {
4525 /* Less than 6 months from now - "August seventh" */
4526 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4527 } else if (beg_today + 86400 * 6 < t) {
4528 /* Less than a month from now - "Sunday, October third" */
4529 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4530 } else if (beg_today + 172800 < t) {
4531 /* Within the next week */
4532 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4533 } else if (beg_today + 86400 < t) {
4534 /* Tomorrow */
4535 res = wait_file(chan, ints, "digits/tomorrow", lang);
4536 } else if (beg_today < t) {
4537 /* Today */
4538 res = wait_file(chan, ints, "digits/today", lang);
4539 } else if (beg_today - 86400 < t) {
4540 /* Yesterday */
4541 res = wait_file(chan, ints, "digits/yesterday", lang);
4542 } else if (beg_today - 86400 * 6 < t) {
4543 /* Within the last week */
4544 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4545 } else if (beg_today - 2628000 < t) {
4546 /* Less than a month ago - "Sunday, October third" */
4547 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4548 } else if (beg_today - 15768000 < t) {
4549 /* Less than 6 months ago - "August seventh" */
4550 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4551 } else {
4552 /* More than 6 months ago - "April nineteenth two thousand three" */
4553 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4554 }
4555 }
4556 break;
4557 case 'q':
4558 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4559 /* XXX As emphasized elsewhere, this should the native way in your
4560 * language to say the date, with changes in what you say, depending
4561 * upon how recent the date is. XXX */
4562 {
4563 struct timeval now;
4564 struct ast_tm tmnow;
4565 time_t beg_today;
4566
4567 now = ast_tvnow();
4568 ast_localtime(&now, &tmnow, tzone);
4569 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4570 /* In any case, it saves not having to do ast_mktime() */
4571 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4572 if (beg_today + 15768000 < t) {
4573 /* More than 6 months from now - "April nineteenth two thousand three" */
4574 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4575 } else if (beg_today + 2628000 < t) {
4576 /* Less than 6 months from now - "August seventh" */
4577 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4578 } else if (beg_today + 86400 * 6 < t) {
4579 /* Less than a month from now - "Sunday, October third" */
4580 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4581 } else if (beg_today + 172800 < t) {
4582 /* Within the next week */
4583 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4584 } else if (beg_today + 86400 < t) {
4585 /* Tomorrow */
4586 res = wait_file(chan, ints, "digits/tomorrow", lang);
4587 } else if (beg_today < t) {
4588 /* Today */
4589 res = wait_file(chan, ints, "digits/today", lang);
4590 } else if (beg_today - 86400 < t) {
4591 /* Yesterday */
4592 res = wait_file(chan, ints, "digits/yesterday", lang);
4593 } else if (beg_today - 86400 * 6 < t) {
4594 /* Within the last week */
4595 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4596 } else if (beg_today - 2628000 < t) {
4597 /* Less than a month ago - "Sunday, October third" */
4598 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4599 } else if (beg_today - 15768000 < t) {
4600 /* Less than 6 months ago - "August seventh" */
4601 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4602 } else {
4603 /* More than 6 months ago - "April nineteenth two thousand three" */
4604 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4605 }
4606 }
4607 break;
4608 case 'R':
4609 res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
4610 break;
4611 case 'S':
4612 /* Seconds */
4613 if (tm.tm_sec == 0) {
4614 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4615 res = wait_file(chan, ints, nextmsg, lang);
4616 } else if (tm.tm_sec < 10) {
4617 res = wait_file(chan, ints, "digits/oh", lang);
4618 if (!res) {
4619 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4620 res = wait_file(chan, ints, nextmsg, lang);
4621 }
4622 } else {
4623 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4624 }
4625 break;
4626 case 'T':
4627 res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
4628 break;
4629 case ' ':
4630 case ' ':
4631 /* Just ignore spaces and tabs */
4632 break;
4633 default:
4634 /* Unknown character */
4635 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4636 }
4637 /* Jump out on DTMF */
4638 if (res) {
4639 break;
4640 }
4641 }
4642 return res;
4643}
4644
4645static char next_item(const char *format)
4646{
4647 const char *next = ast_skip_blanks(format);
4648 return *next;
4649}
4650
4651/*! \brief Danish syntax */
4652int 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)
4653{
4654 struct timeval when = { t, 0 };
4655 struct ast_tm tm;
4656 int res=0, offset, sndoffset;
4657 char sndfile[256], nextmsg[256];
4658
4659 if (!format)
4660 format = "A dBY HMS";
4661
4662 ast_localtime(&when, &tm, tzone);
4663
4664 for (offset=0 ; format[offset] != '\0' ; offset++) {
4665 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4666 switch (format[offset]) {
4667 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4668 case '\'':
4669 /* Literal name of a sound file */
4670 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4671 sndfile[sndoffset] = format[offset];
4672 }
4673 sndfile[sndoffset] = '\0';
4674 res = wait_file(chan, ints, sndfile, lang);
4675 break;
4676 case 'A':
4677 case 'a':
4678 /* Sunday - Saturday */
4679 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4680 res = wait_file(chan, ints, nextmsg, lang);
4681 break;
4682 case 'B':
4683 case 'b':
4684 case 'h':
4685 /* January - December */
4686 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4687 res = wait_file(chan, ints, nextmsg, lang);
4688 break;
4689 case 'm':
4690 /* Month enumerated */
4691 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4692 break;
4693 case 'd':
4694 case 'e':
4695 /* First - Thirtyfirst */
4696 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4697 break;
4698 case 'Y':
4699 /* Year */
4700 {
4701 int year = tm.tm_year + 1900;
4702 if (year > 1999) { /* year 2000 and later */
4703 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4704 } else {
4705 if (year < 1100) {
4706 /* I'm not going to handle 1100 and prior */
4707 /* We'll just be silent on the year, instead of bombing out. */
4708 } else {
4709 /* year 1100 to 1999. will anybody need this?!? */
4710 /* say 1967 as 'nineteen hundred seven and sixty' */
4711 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4712 res = wait_file(chan, ints, nextmsg, lang);
4713 if (!res) {
4714 res = wait_file(chan, ints, "digits/hundred", lang);
4715 if (!res && year % 100 != 0) {
4716 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4717 }
4718 }
4719 }
4720 }
4721 }
4722 break;
4723 case 'I':
4724 case 'l':
4725 /* 12-Hour */
4726 res = wait_file(chan, ints, "digits/oclock", lang);
4727 if (tm.tm_hour == 0)
4728 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4729 else if (tm.tm_hour > 12)
4730 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4731 else
4732 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4733 if (!res) {
4734 res = wait_file(chan, ints, nextmsg, lang);
4735 }
4736 break;
4737 case 'H':
4738 /* 24-Hour, single digit hours preceded by "oh" (0) */
4739 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4740 res = wait_file(chan, ints, "digits/0", lang);
4741 }
4742 /* FALLTRHU */
4743 case 'k':
4744 /* 24-Hour */
4745 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4746 break;
4747 case 'M':
4748 /* Minute */
4749 if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4750 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4751 }
4752 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4753 if (tm.tm_min == 1) {
4754 res = wait_file(chan, ints, "minute", lang);
4755 } else {
4756 res = wait_file(chan, ints, "minutes", lang);
4757 }
4758 }
4759 break;
4760 case 'P':
4761 case 'p':
4762 /* AM/PM */
4763 if (tm.tm_hour > 11)
4764 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4765 else
4766 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4767 res = wait_file(chan, ints, nextmsg, lang);
4768 break;
4769 case 'Q':
4770 /* Shorthand for "Today", "Yesterday", or AdBY */
4771 /* XXX As emphasized elsewhere, this should the native way in your
4772 * language to say the date, with changes in what you say, depending
4773 * upon how recent the date is. XXX */
4774 {
4775 struct timeval now = ast_tvnow();
4776 struct ast_tm tmnow;
4777 time_t beg_today;
4778
4779 ast_localtime(&now, &tmnow, tzone);
4780 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4781 /* In any case, it saves not having to do ast_mktime() */
4782 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4783 if (beg_today < t) {
4784 /* Today */
4785 res = wait_file(chan, ints, "digits/today", lang);
4786 } else if (beg_today - 86400 < t) {
4787 /* Yesterday */
4788 res = wait_file(chan, ints, "digits/yesterday", lang);
4789 } else {
4790 res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4791 }
4792 }
4793 break;
4794 case 'q':
4795 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4796 /* XXX As emphasized elsewhere, this should the native way in your
4797 * language to say the date, with changes in what you say, depending
4798 * upon how recent the date is. XXX */
4799 {
4800 struct timeval now = ast_tvnow();
4801 struct ast_tm tmnow;
4802 time_t beg_today;
4803
4804 ast_localtime(&now, &tmnow, tzone);
4805 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4806 /* In any case, it saves not having to do ast_mktime() */
4807 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4808 if (beg_today < t) {
4809 /* Today */
4810 } else if ((beg_today - 86400) < t) {
4811 /* Yesterday */
4812 res = wait_file(chan, ints, "digits/yesterday", lang);
4813 } else if (beg_today - 86400 * 6 < t) {
4814 /* Within the last week */
4815 res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
4816 } else {
4817 res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4818 }
4819 }
4820 break;
4821 case 'R':
4822 res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
4823 break;
4824 case 'S':
4825 /* Seconds */
4826 res = wait_file(chan, ints, "digits/and", lang);
4827 if (!res) {
4828 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4829 if (!res) {
4830 res = wait_file(chan, ints, "seconds", lang);
4831 }
4832 }
4833 break;
4834 case 'T':
4835 res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
4836 break;
4837 case ' ':
4838 case ' ':
4839 /* Just ignore spaces and tabs */
4840 break;
4841 default:
4842 /* Unknown character */
4843 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4844 }
4845 /* Jump out on DTMF */
4846 if (res) {
4847 break;
4848 }
4849 }
4850 return res;
4851}
4852
4853/*! \brief German syntax */
4854int 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)
4855{
4856 struct timeval when = { t, 0 };
4857 struct ast_tm tm;
4858 int res=0, offset, sndoffset;
4859 char sndfile[256], nextmsg[256];
4860
4861 if (!format)
4862 format = "A dBY HMS";
4863
4864 ast_localtime(&when, &tm, tzone);
4865
4866 for (offset=0 ; format[offset] != '\0' ; offset++) {
4867 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4868 switch (format[offset]) {
4869 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4870 case '\'':
4871 /* Literal name of a sound file */
4872 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4873 sndfile[sndoffset] = format[offset];
4874 }
4875 sndfile[sndoffset] = '\0';
4876 res = wait_file(chan, ints, sndfile, lang);
4877 break;
4878 case 'A':
4879 case 'a':
4880 /* Sunday - Saturday */
4881 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4882 res = wait_file(chan, ints, nextmsg, lang);
4883 break;
4884 case 'B':
4885 case 'b':
4886 case 'h':
4887 /* January - December */
4888 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4889 res = wait_file(chan, ints, nextmsg, lang);
4890 break;
4891 case 'm':
4892 /* Month enumerated */
4893 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4894 break;
4895 case 'd':
4896 case 'e':
4897 /* First - Thirtyfirst */
4898 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4899 break;
4900 case 'Y':
4901 /* Year */
4902 {
4903 int year = tm.tm_year + 1900;
4904 if (year > 1999) { /* year 2000 and later */
4905 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4906 } else {
4907 if (year < 1100) {
4908 /* I'm not going to handle 1100 and prior */
4909 /* We'll just be silent on the year, instead of bombing out. */
4910 } else {
4911 /* year 1100 to 1999. will anybody need this?!? */
4912 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4913 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4914 res = wait_file(chan, ints, nextmsg, lang);
4915 if (!res) {
4916 res = wait_file(chan, ints, "digits/hundred", lang);
4917 if (!res && year % 100 != 0) {
4918 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4919 }
4920 }
4921 }
4922 }
4923 }
4924 break;
4925 case 'I':
4926 case 'l':
4927 /* 12-Hour */
4928 if (tm.tm_hour == 0)
4929 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4930 else if (tm.tm_hour == 1)
4931 ast_copy_string(nextmsg, "digits/1N", sizeof(nextmsg));
4932 else if (tm.tm_hour > 12)
4933 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4934 else
4935 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4936 res = wait_file(chan, ints, nextmsg, lang);
4937 if (!res) {
4938 res = wait_file(chan, ints, "digits/oclock", lang);
4939 }
4940 break;
4941 case 'H':
4942 case 'k':
4943 /* 24-Hour */
4944 if (tm.tm_hour == 1) {
4945 res = wait_file(chan, ints, "digits/1N", lang);
4946 } else {
4947 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4948 }
4949 if (!res) {
4950 res = wait_file(chan, ints, "digits/oclock", lang);
4951 }
4952 break;
4953 case 'M':
4954 /* Minute */
4955 if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4956 res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say minutes */
4957 } else if (tm.tm_min > 0) {
4958 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4959 }
4960
4961 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4962 if (tm.tm_min == 1) {
4963 res = wait_file(chan, ints, "minute", lang);
4964 } else {
4965 res = wait_file(chan, ints, "minutes", lang);
4966 }
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 AdBY */
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 ast_localtime(&now, &tmnow, tzone);
4989 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4990 /* In any case, it saves not having to do ast_mktime() */
4991 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4992 if (beg_today < t) {
4993 /* Today */
4994 res = wait_file(chan, ints, "digits/today", lang);
4995 } else if (beg_today - 86400 < t) {
4996 /* Yesterday */
4997 res = wait_file(chan, ints, "digits/yesterday", lang);
4998 } else {
4999 res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
5000 }
5001 }
5002 break;
5003 case 'q':
5004 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5005 /* XXX As emphasized elsewhere, this should the native way in your
5006 * language to say the date, with changes in what you say, depending
5007 * upon how recent the date is. XXX */
5008 {
5009 struct timeval now = ast_tvnow();
5010 struct ast_tm tmnow;
5011 time_t beg_today;
5012
5013 ast_localtime(&now, &tmnow, tzone);
5014 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5015 /* In any case, it saves not having to do ast_mktime() */
5016 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5017 if (beg_today < t) {
5018 /* Today */
5019 } else if ((beg_today - 86400) < t) {
5020 /* Yesterday */
5021 res = wait_file(chan, ints, "digits/yesterday", lang);
5022 } else if (beg_today - 86400 * 6 < t) {
5023 /* Within the last week */
5024 res = ast_say_date_with_format_de(chan, t, ints, lang, "A", tzone);
5025 } else {
5026 res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
5027 }
5028 }
5029 break;
5030 case 'R':
5031 res = ast_say_date_with_format_de(chan, t, ints, lang, "HM", tzone);
5032 break;
5033 case 'S':
5034 /* Seconds */
5035 res = wait_file(chan, ints, "digits/and", lang);
5036 if (!res) {
5037 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5038 if (!res) {
5039 res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
5040 }
5041 }
5042 break;
5043 case 'T':
5044 res = ast_say_date_with_format_de(chan, t, ints, lang, "HMS", tzone);
5045 break;
5046 case ' ':
5047 case ' ':
5048 /* Just ignore spaces and tabs */
5049 break;
5050 default:
5051 /* Unknown character */
5052 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5053 }
5054 /* Jump out on DTMF */
5055 if (res) {
5056 break;
5057 }
5058 }
5059 return res;
5060}
5061
5062/* Icelandic syntax */
5063int 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)
5064{
5065 struct timeval when = { t, 0 };
5066 struct ast_tm tm;
5067 int res=0, offset, sndoffset;
5068 char sndfile[256], nextmsg[256];
5069
5070 if (!format)
5071 format = "A dBY HMS";
5072
5073 ast_localtime(&when, &tm, tzone);
5074
5075 for (offset=0 ; format[offset] != '\0' ; offset++) {
5076 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5077 switch (format[offset]) {
5078 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5079 case '\'':
5080 /* Literal name of a sound file */
5081 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5082 sndfile[sndoffset] = format[offset];
5083 }
5084 sndfile[sndoffset] = '\0';
5085 res = wait_file(chan, ints, sndfile, lang);
5086 break;
5087 case 'A':
5088 case 'a':
5089 /* Sunday - Saturday */
5090 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5091 res = wait_file(chan, ints, nextmsg, lang);
5092 break;
5093 case 'B':
5094 case 'b':
5095 case 'h':
5096 /* January - December */
5097 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5098 res = wait_file(chan, ints, nextmsg, lang);
5099 break;
5100 case 'm':
5101 /* Month enumerated */
5102 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
5103 break;
5104 case 'd':
5105 case 'e':
5106 /* First - Thirtyfirst */
5107 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
5108 break;
5109 case 'Y':
5110 /* Year */
5111 {
5112 int year = tm.tm_year + 1900;
5113 if (year > 1999) { /* year 2000 and later */
5114 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
5115 } else {
5116 if (year < 1100) {
5117 /* I'm not going to handle 1100 and prior */
5118 /* We'll just be silent on the year, instead of bombing out. */
5119 } else {
5120 /* year 1100 to 1999. will anybody need this?!? */
5121 /* say 1967 as 'nineteen hundred seven and sixty' */
5122 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
5123 res = wait_file(chan, ints, nextmsg, lang);
5124 if (!res) {
5125 res = wait_file(chan, ints, "digits/hundred", lang);
5126 if (!res && year % 100 != 0) {
5127 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
5128 }
5129 }
5130 }
5131 }
5132 }
5133 break;
5134 case 'I':
5135 case 'l':
5136 /* 12-Hour */
5137 res = wait_file(chan, ints, "digits/oclock", lang);
5138 if (tm.tm_hour == 0)
5139 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5140 else if (tm.tm_hour > 12)
5141 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5142 else
5143 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5144 if (!res) {
5145 res = wait_file(chan, ints, nextmsg, lang);
5146 }
5147 break;
5148 case 'H':
5149 /* 24-Hour, single digit hours preceeded by "oh" (0) */
5150 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
5151 res = wait_file(chan, ints, "digits/0", lang);
5152 }
5153 /* FALLTRHU */
5154 case 'k':
5155 /* 24-Hour */
5156 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5157 break;
5158 case 'M':
5159 /* Minute */
5160 if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
5161 if (tm.tm_min < 10)
5162 res = wait_file(chan, ints, "digits/0", lang);
5163 /* Gender depends on whether or not seconds follow */
5164 if (next_item(&format[offset + 1]) == 'S')
5165 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5166 else
5167 res = ast_say_number(chan, tm.tm_min, ints, lang, "n");
5168 }
5169 if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5170 /* Say minute/minutes depending on whether minutes end in 1 */
5171 if ((tm.tm_min % 10 == 1) && (tm.tm_min != 11)) {
5172 res = wait_file(chan, ints, "minute", lang);
5173 } else {
5174 res = wait_file(chan, ints, "minutes", lang);
5175 }
5176 }
5177 break;
5178 case 'P':
5179 case 'p':
5180 /* AM/PM */
5181 if (tm.tm_hour > 11)
5182 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5183 else
5184 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5185 res = wait_file(chan, ints, nextmsg, lang);
5186 break;
5187 case 'Q':
5188 /* Shorthand for "Today", "Yesterday", or AdBY */
5189 /* XXX As emphasized elsewhere, this should the native way in your
5190 * language to say the date, with changes in what you say, depending
5191 * upon how recent the date is. XXX */
5192 {
5193 struct timeval now = ast_tvnow();
5194 struct ast_tm tmnow;
5195 time_t beg_today;
5196
5197 ast_localtime(&now, &tmnow, tzone);
5198 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5199 /* In any case, it saves not having to do ast_mktime() */
5200 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5201 if (beg_today < t) {
5202 /* Today */
5203 res = wait_file(chan, ints, "digits/today", lang);
5204 } else if (beg_today - 86400 < t) {
5205 /* Yesterday */
5206 res = wait_file(chan, ints, "digits/yesterday", lang);
5207 } else {
5208 res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5209 }
5210 }
5211 break;
5212 case 'q':
5213 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5214 /* XXX As emphasized elsewhere, this should the native way in your
5215 * language to say the date, with changes in what you say, depending
5216 * upon how recent the date is. XXX */
5217 {
5218 struct timeval now = ast_tvnow();
5219 struct ast_tm tmnow;
5220 time_t beg_today;
5221
5222 ast_localtime(&now, &tmnow, tzone);
5223 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5224 /* In any case, it saves not having to do ast_mktime() */
5225 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5226 if (beg_today < t) {
5227 /* Today */
5228 } else if ((beg_today - 86400) < t) {
5229 /* Yesterday */
5230 res = wait_file(chan, ints, "digits/yesterday", lang);
5231 } else if (beg_today - 86400 * 6 < t) {
5232 /* Within the last week */
5233 res = ast_say_date_with_format_is(chan, t, ints, lang, "A", tzone);
5234 } else {
5235 res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5236 }
5237 }
5238 break;
5239 case 'R':
5240 res = ast_say_date_with_format_is(chan, t, ints, lang, "HM", tzone);
5241 break;
5242 case 'S':
5243 /* Seconds */
5244 res = wait_file(chan, ints, "digits/and", lang);
5245 if (!res) {
5246 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5247 /* Say minute/minutes depending on whether seconds end in 1 */
5248 if (!res && (tm.tm_sec % 10 == 1) && (tm.tm_sec != 11)) {
5249 res = wait_file(chan, ints, "second", lang);
5250 } else {
5251 res = wait_file(chan, ints, "seconds", lang);
5252 }
5253 }
5254 break;
5255 case 'T':
5256 res = ast_say_date_with_format_is(chan, t, ints, lang, "HMS", tzone);
5257 break;
5258 case ' ':
5259 case ' ':
5260 /* Just ignore spaces and tabs */
5261 break;
5262 default:
5263 /* Unknown character */
5264 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5265 }
5266 /* Jump out on DTMF */
5267 if (res) {
5268 break;
5269 }
5270 }
5271 return res;
5272}
5273
5274/*! \brief Thai syntax */
5275int 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)
5276{
5277 struct timeval when = { t, 0 };
5278 struct ast_tm tm;
5279 int res=0, offset, sndoffset;
5280 char sndfile[256], nextmsg[256];
5281
5282 if (format == NULL)
5283 format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
5284
5285 ast_localtime(&when, &tm, tzone);
5286
5287 for (offset=0 ; format[offset] != '\0' ; offset++) {
5288 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5289 switch (format[offset]) {
5290 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5291 case '\'':
5292 /* Literal name of a sound file */
5293 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5294 sndfile[sndoffset] = format[offset];
5295 }
5296 sndfile[sndoffset] = '\0';
5297 res = wait_file(chan, ints, sndfile, lang);
5298 break;
5299 case 'A':
5300 case 'a':
5301 /* Sunday - Saturday */
5302 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5303 res = wait_file(chan, ints, nextmsg, lang);
5304 break;
5305 case 'B':
5306 case 'b':
5307 case 'h':
5308 /* January - December */
5309 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5310 res = wait_file(chan, ints, nextmsg, lang);
5311 break;
5312 case 'm':
5313 /* Month enumerated */
5314 res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
5315 break;
5316 case 'd':
5317 case 'e':
5318 /* First - Thirtyfirst */
5319 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5320 break;
5321 case 'Y':
5322 /* Year */
5323 res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
5324 break;
5325 case 'I':
5326 case 'l':
5327 /* 12-Hour */
5328 if (tm.tm_hour == 0)
5329 ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5330 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5331 res = wait_file(chan, ints, nextmsg, lang);
5332 break;
5333 case 'H':
5334 case 'k':
5335 /* 24-Hour */
5336 if (tm.tm_hour == 0)
5337 ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5338 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5339 res = wait_file(chan, ints, nextmsg, lang);
5340 break;
5341 case 'M':
5342 case 'N':
5343 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5344 break;
5345 case 'P':
5346 case 'p':
5347 break;
5348 case 'Q':
5349 /* Shorthand for "Today", "Yesterday", or ABdY */
5350 /* XXX As emphasized elsewhere, this should the native way in your
5351 * language to say the date, with changes in what you say, depending
5352 * upon how recent the date is. XXX */
5353 {
5354 struct timeval now = ast_tvnow();
5355 struct ast_tm tmnow;
5356 time_t beg_today;
5357
5358 ast_localtime(&now, &tmnow, tzone);
5359 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5360 /* In any case, it saves not having to do ast_mktime() */
5361 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5362 if (beg_today < t) {
5363 /* Today */
5364 res = wait_file(chan, ints, "digits/today", lang);
5365 } else if (beg_today - 86400 < t) {
5366 /* Yesterday */
5367 res = wait_file(chan, ints, "digits/yesterday", lang);
5368 } else if (beg_today - 86400 * 6 < t) {
5369 /* Within the last week */
5370 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5371 } else if (beg_today - 2628000 < t) {
5372 /* Less than a month ago - "Sunday, October third" */
5373 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5374 } else if (beg_today - 15768000 < t) {
5375 /* Less than 6 months ago - "August seventh" */
5376 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5377 } else {
5378 /* More than 6 months ago - "April nineteenth two thousand three" */
5379 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5380 }
5381 }
5382 break;
5383 case 'q':
5384 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5385 /* XXX As emphasized elsewhere, this should the native way in your
5386 * language to say the date, with changes in what you say, depending
5387 * upon how recent the date is. XXX */
5388 {
5389 struct timeval now = ast_tvnow();
5390 struct ast_tm tmnow;
5391 time_t beg_today;
5392
5393 ast_localtime(&now, &tmnow, tzone);
5394 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5395 /* In any case, it saves not having to do ast_mktime() */
5396 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5397 if (beg_today < t) {
5398 /* Today */
5399 } else if ((beg_today - 86400) < t) {
5400 /* Yesterday */
5401 res = wait_file(chan, ints, "digits/yesterday", lang);
5402 } else if (beg_today - 86400 * 6 < t) {
5403 /* Within the last week */
5404 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5405 } else if (beg_today - 2628000 < t) {
5406 /* Less than a month ago - "Sunday, October third" */
5407 res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5408 } else if (beg_today - 15768000 < t) {
5409 /* Less than 6 months ago - "August seventh" */
5410 res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5411 } else {
5412 /* More than 6 months ago - "April nineteenth two thousand three" */
5413 res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5414 }
5415 }
5416 break;
5417 case 'R':
5418 res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
5419 break;
5420 case 'S':
5421 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
5422 break;
5423 case 'T':
5424 res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
5425 break;
5426 case ' ':
5427 case ' ':
5428 /* Just ignore spaces and tabs */
5429 break;
5430 default:
5431 /* Unknown character */
5432 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5433 }
5434 /* Jump out on DTMF */
5435 if (res) {
5436 break;
5437 }
5438 }
5439 return res;
5440}
5441
5442/*! \brief ast_say_date_with_format_he Say formatted date in Hebrew
5443 *
5444 * \ref ast_say_date_with_format_en for the details of the options
5445 *
5446 * Changes from the English version:
5447 *
5448 * - don't replicate in here the logic of ast_say_number_full_he
5449 *
5450 * - year is always 4-digit (because it's simpler)
5451 *
5452 * - added c, x, and X. Mainly for my tests
5453 *
5454 * - The standard "long" format used in Hebrew is AdBY, rather than ABdY
5455 *
5456 * \todo
5457 * - A "ha" is missing in the standard date format, before the 'd'.
5458 * - The numbers of 3000--19000 are not handled well
5459 */
5460int 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)
5461{
5462#define IL_DATE_STR "AdBY"
5463#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 */
5464#define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
5465 /* TODO: This whole function is cut&paste from
5466 * ast_say_date_with_format_en . Is that considered acceptable?
5467 **/
5468 struct timeval when = { t, 0 };
5469 struct ast_tm tm;
5470 int res = 0, offset, sndoffset;
5471 char sndfile[256], nextmsg[256];
5472
5473 if (!format) {
5474 format = IL_DATE_STR_FULL;
5475 }
5476
5477 ast_localtime(&when, &tm, tzone);
5478
5479 for (offset = 0; format[offset] != '\0'; offset++) {
5480 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5481 switch (format[offset]) {
5482 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5483 case '\'':
5484 /* Literal name of a sound file */
5485 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5486 sndfile[sndoffset] = format[offset];
5487 }
5488 sndfile[sndoffset] = '\0';
5489 res = wait_file(chan, ints, sndfile, lang);
5490 break;
5491 case 'A':
5492 case 'a':
5493 /* Sunday - Saturday */
5494 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5495 res = wait_file(chan, ints, nextmsg, lang);
5496 break;
5497 case 'B':
5498 case 'b':
5499 case 'h':
5500 /* January - December */
5501 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5502 res = wait_file(chan, ints, nextmsg, lang);
5503 break;
5504 case 'd':
5505 case 'e': /* Day of the month */
5506 /* I'm not sure exactly what the parameters
5507 * audiofd and ctrlfd to
5508 * ast_say_number_full_he mean, but it seems
5509 * safe to pass -1 there.
5510 *
5511 * At least in one of the paths :-(
5512 */
5513 res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
5514 break;
5515 case 'Y': /* Year */
5516 res = ast_say_number_full_he(chan, tm.tm_year + 1900, ints, lang, "f", -1, -1);
5517 break;
5518 case 'I':
5519 case 'l': /* 12-Hour -> we do not support 12 hour based languages in Hebrew */
5520 case 'H':
5521 case 'k': /* 24-Hour */
5522 res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
5523 break;
5524 case 'M': /* Minute */
5525 if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
5526 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5527 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5528 break;
5529 case 'P':
5530 case 'p':
5531 /* AM/PM - There is no AM/PM in Hebrew... */
5532 break;
5533 case 'Q':
5534 /* Shorthand for "Today", "Yesterday", or "date" */
5535 case 'q':
5536 /* Shorthand for "" (today), "Yesterday", A
5537 * (weekday), or "date" */
5538 /* XXX As emphasized elsewhere, this should the native way in your
5539 * language to say the date, with changes in what you say, depending
5540 * upon how recent the date is. XXX */
5541 {
5542 struct timeval now = ast_tvnow();
5543 struct ast_tm tmnow;
5544 time_t beg_today;
5545 char todo = format[offset]; /* The letter to format*/
5546
5547 ast_localtime(&now, &tmnow, tzone);
5548 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5549 /* In any case, it saves not having to do ast_mktime() */
5550 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5551 if (beg_today < t) {
5552 /* Today */
5553 if (todo == 'Q') {
5554 res = wait_file(chan, ints, "digits/today", lang);
5555 }
5556 } else if (beg_today - 86400 < t) {
5557 /* Yesterday */
5558 res = wait_file(chan, ints, "digits/yesterday", lang);
5559 } else if ((todo != 'Q') && (beg_today - 86400 * 6 < t)) {
5560 /* Within the last week */
5561 res = ast_say_date_with_format_he(chan, t, ints, lang, "A", tzone);
5562 } else {
5563 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5564 }
5565 }
5566 break;
5567 case 'R':
5568 res = ast_say_date_with_format_he(chan, t, ints, lang, "HM", tzone);
5569 break;
5570 case 'S': /* Seconds */
5571 res = ast_say_number_full_he(chan, tm.tm_sec,
5572 ints, lang, "f", -1, -1
5573 );
5574 break;
5575 case 'T':
5576 res = ast_say_date_with_format_he(chan, t, ints, lang, "HMS", tzone);
5577 break;
5578 /* c, x, and X seem useful for testing. Not sure
5579 * if they're good for the general public */
5580 case 'c':
5581 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR_FULL, tzone);
5582 break;
5583 case 'x':
5584 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5585 break;
5586 case 'X': /* Currently not locale-dependent...*/
5587 res = ast_say_date_with_format_he(chan, t, ints, lang, IL_TIME_STR, tzone);
5588 break;
5589 case ' ':
5590 case ' ':
5591 /* Just ignore spaces and tabs */
5592 break;
5593 default:
5594 /* Unknown character */
5595 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5596 }
5597 /* Jump out on DTMF */
5598 if (res) {
5599 break;
5600 }
5601 }
5602 return res;
5603}
5604
5605
5606/*! \brief Spanish syntax */
5607int 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)
5608{
5609 struct timeval when = { t, 0 };
5610 struct ast_tm tm;
5611 int res=0, offset, sndoffset;
5612 char sndfile[256], nextmsg[256];
5613
5614 if (format == NULL)
5615 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
5616
5617 ast_localtime(&when, &tm, tzone);
5618
5619 for (offset=0 ; format[offset] != '\0' ; offset++) {
5620 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5621 switch (format[offset]) {
5622 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5623 case '\'':
5624 /* Literal name of a sound file */
5625 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5626 sndfile[sndoffset] = format[offset];
5627 }
5628 sndfile[sndoffset] = '\0';
5629 snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
5630 res = wait_file(chan, ints, nextmsg, lang);
5631 break;
5632 case 'A':
5633 case 'a':
5634 /* Sunday - Saturday */
5635 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5636 res = wait_file(chan, ints, nextmsg, lang);
5637 break;
5638 case 'B':
5639 case 'b':
5640 case 'h':
5641 /* January - December */
5642 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5643 res = wait_file(chan, ints, nextmsg, lang);
5644 break;
5645 case 'm':
5646 /* First - Twelfth */
5647 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5648 res = wait_file(chan, ints, nextmsg, lang);
5649 break;
5650 case 'd':
5651 case 'e':
5652 /* First - Thirtyfirst */
5653 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5654 break;
5655 case 'Y':
5656 /* Year */
5657 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5658 break;
5659 case 'I':
5660 case 'l':
5661 /* 12-Hour */
5662 if (tm.tm_hour == 0)
5663 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5664 else if (tm.tm_hour == 1 || tm.tm_hour == 13)
5665 snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
5666 else if (tm.tm_hour > 12)
5667 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5668 else
5669 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5670 res = wait_file(chan, ints, nextmsg, lang);
5671 break;
5672 case 'H':
5673 case 'k':
5674 /* 24-Hour */
5675 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
5676 break;
5677 case 'M':
5678 /* Minute */
5679 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5680 break;
5681 case 'P':
5682 case 'p':
5683 /* AM/PM */
5684 if (tm.tm_hour > 18)
5685 res = wait_file(chan, ints, "digits/p-m", lang);
5686 else if (tm.tm_hour > 12)
5687 res = wait_file(chan, ints, "digits/afternoon", lang);
5688 else if (tm.tm_hour)
5689 res = wait_file(chan, ints, "digits/a-m", lang);
5690 break;
5691 case 'Q':
5692 /* Shorthand for "Today", "Yesterday", or ABdY */
5693 /* XXX As emphasized elsewhere, this should the native way in your
5694 * language to say the date, with changes in what you say, depending
5695 * upon how recent the date is. XXX */
5696 {
5697 struct timeval now = ast_tvnow();
5698 struct ast_tm tmnow;
5699 time_t beg_today;
5700
5701 ast_localtime(&now, &tmnow, tzone);
5702 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5703 /* In any case, it saves not having to do ast_mktime() */
5704 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5705 if (beg_today < t) {
5706 /* Today */
5707 res = wait_file(chan, ints, "digits/today", lang);
5708 } else if (beg_today - 86400 < t) {
5709 /* Yesterday */
5710 res = wait_file(chan, ints, "digits/yesterday", lang);
5711 } else {
5712 res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5713 }
5714 }
5715 break;
5716 case 'q':
5717 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5718 /* XXX As emphasized elsewhere, this should the native way in your
5719 * language to say the date, with changes in what you say, depending
5720 * upon how recent the date is. XXX */
5721 {
5722 struct timeval now = ast_tvnow();
5723 struct ast_tm tmnow;
5724 time_t beg_today;
5725
5726 ast_localtime(&now, &tmnow, tzone);
5727 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5728 /* In any case, it saves not having to do ast_mktime() */
5729 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5730 if (beg_today < t) {
5731 /* Today */
5732 res = wait_file(chan, ints, "digits/today", lang);
5733 } else if ((beg_today - 86400) < t) {
5734 /* Yesterday */
5735 res = wait_file(chan, ints, "digits/yesterday", lang);
5736 } else if (beg_today - 86400 * 6 < t) {
5737 /* Within the last week */
5738 res = ast_say_date_with_format_es(chan, t, ints, lang, "A", tzone);
5739 } else {
5740 res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5741 }
5742 }
5743 break;
5744 case 'R':
5745 res = ast_say_date_with_format_es(chan, t, ints, lang, "H 'digits/y' M", tzone);
5746 break;
5747 case 'S':
5748 /* Seconds */
5749 if (tm.tm_sec == 0) {
5750 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5751 res = wait_file(chan, ints, nextmsg, lang);
5752 } else if (tm.tm_sec < 10) {
5753 res = wait_file(chan, ints, "digits/oh", lang);
5754 if (!res) {
5755 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5756 res = wait_file(chan, ints, nextmsg, lang);
5757 }
5758 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5759 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5760 res = wait_file(chan, ints, nextmsg, lang);
5761 } else {
5762 int ten, one;
5763 ten = (tm.tm_sec / 10) * 10;
5764 one = (tm.tm_sec % 10);
5765 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
5766 res = wait_file(chan, ints, nextmsg, lang);
5767 if (!res) {
5768 /* Fifty, not fifty-zero */
5769 if (one != 0) {
5770 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
5771 res = wait_file(chan, ints, nextmsg, lang);
5772 }
5773 }
5774 }
5775 break;
5776 case 'T':
5777 res = ast_say_date_with_format_es(chan, t, ints, lang, "HMS", tzone);
5778 break;
5779 case ' ':
5780 case ' ':
5781 /* Just ignore spaces and tabs */
5782 break;
5783 default:
5784 /* Unknown character */
5785 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5786 }
5787 /* Jump out on DTMF */
5788 if (res) {
5789 break;
5790 }
5791 }
5792 return res;
5793}
5794
5795/*! \brief French syntax
5796oclock = heure
5797*/
5798int 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)
5799{
5800 struct timeval when = { t, 0 };
5801 struct ast_tm tm;
5802 int res=0, offset, sndoffset;
5803 char sndfile[256], nextmsg[256];
5804
5805 if (format == NULL)
5806 format = "AdBY 'digits/at' IMp";
5807
5808 ast_localtime(&when, &tm, tzone);
5809
5810 for (offset=0 ; format[offset] != '\0' ; offset++) {
5811 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5812 switch (format[offset]) {
5813 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5814 case '\'':
5815 /* Literal name of a sound file */
5816 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5817 sndfile[sndoffset] = format[offset];
5818 }
5819 sndfile[sndoffset] = '\0';
5820 res = wait_file(chan, ints, sndfile, lang);
5821 break;
5822 case 'A':
5823 case 'a':
5824 /* Sunday - Saturday */
5825 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5826 res = wait_file(chan, ints, nextmsg, lang);
5827 break;
5828 case 'B':
5829 case 'b':
5830 case 'h':
5831 /* January - December */
5832 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5833 res = wait_file(chan, ints, nextmsg, lang);
5834 break;
5835 case 'm':
5836 /* First - Twelfth */
5837 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5838 res = wait_file(chan, ints, nextmsg, lang);
5839 break;
5840 case 'd':
5841 case 'e':
5842 /* First */
5843 if (tm.tm_mday == 1) {
5844 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5845 res = wait_file(chan, ints, nextmsg, lang);
5846 } else {
5847 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
5848 }
5849 break;
5850 case 'Y':
5851 /* Year */
5852 if (tm.tm_year > 99) {
5853 res = wait_file(chan, ints, "digits/2", lang);
5854 if (!res) {
5855 res = wait_file(chan, ints, "digits/thousand", lang);
5856 }
5857 if (tm.tm_year > 100) {
5858 if (!res) {
5859 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
5860 }
5861 }
5862 } else {
5863 if (tm.tm_year < 1) {
5864 /* I'm not going to handle 1900 and prior */
5865 /* We'll just be silent on the year, instead of bombing out. */
5866 } else {
5867 res = wait_file(chan, ints, "digits/thousand", lang);
5868 if (!res) {
5869 wait_file(chan, ints, "digits/9", lang);
5870 wait_file(chan, ints, "digits/hundred", lang);
5871 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
5872 }
5873 }
5874 }
5875 break;
5876 case 'I':
5877 case 'l':
5878 /* 12-Hour */
5879 if (tm.tm_hour == 0)
5880 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5881 else if (tm.tm_hour > 12)
5882 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5883 else
5884 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5885 res = wait_file(chan, ints, nextmsg, lang);
5886 if (!res)
5887 res = wait_file(chan, ints, "digits/oclock", lang);
5888 break;
5889 case 'H':
5890 case 'k':
5891 /* 24-Hour */
5892 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
5893 if (!res)
5894 res = wait_file(chan, ints, "digits/oclock", lang);
5895 break;
5896 case 'M':
5897 /* Minute */
5898 if (tm.tm_min == 0) {
5899 break;
5900 }
5901 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
5902 break;
5903 case 'P':
5904 case 'p':
5905 /* AM/PM */
5906 if (tm.tm_hour > 11)
5907 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5908 else
5909 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5910 res = wait_file(chan, ints, nextmsg, lang);
5911 break;
5912 case 'Q':
5913 /* Shorthand for "Today", "Yesterday", or AdBY */
5914 /* XXX As emphasized elsewhere, this should the native way in your
5915 * language to say the date, with changes in what you say, depending
5916 * upon how recent the date is. XXX */
5917 {
5918 struct timeval now = ast_tvnow();
5919 struct ast_tm tmnow;
5920 time_t beg_today;
5921
5922 ast_localtime(&now, &tmnow, tzone);
5923 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5924 /* In any case, it saves not having to do ast_mktime() */
5925 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5926 if (beg_today < t) {
5927 /* Today */
5928 res = wait_file(chan, ints, "digits/today", lang);
5929 } else if (beg_today - 86400 < t) {
5930 /* Yesterday */
5931 res = wait_file(chan, ints, "digits/yesterday", lang);
5932 } else {
5933 res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
5934 }
5935 }
5936 break;
5937 case 'q':
5938 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5939 /* XXX As emphasized elsewhere, this should the native way in your
5940 * language to say the date, with changes in what you say, depending
5941 * upon how recent the date is. XXX */
5942 {
5943 struct timeval now = ast_tvnow();
5944 struct ast_tm tmnow;
5945 time_t beg_today;
5946
5947 ast_localtime(&now, &tmnow, tzone);
5948 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5949 /* In any case, it saves not having to do ast_mktime() */
5950 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5951 if (beg_today < t) {
5952 /* Today */
5953 } else if ((beg_today - 86400) < t) {
5954 /* Yesterday */
5955 res = wait_file(chan, ints, "digits/yesterday", lang);
5956 } else if (beg_today - 86400 * 6 < t) {
5957 /* Within the last week */
5958 res = ast_say_date_with_format_fr(chan, t, ints, lang, "A", tzone);
5959 } else {
5960 res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
5961 }
5962 }
5963 break;
5964 case 'R':
5965 res = ast_say_date_with_format_fr(chan, t, ints, lang, "HM", tzone);
5966 break;
5967 case 'S':
5968 /* Seconds */
5969 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
5970 if (!res) {
5971 res = wait_file(chan, ints, "second", lang);
5972 }
5973 break;
5974 case 'T':
5975 res = ast_say_date_with_format_fr(chan, t, ints, lang, "HMS", tzone);
5976 break;
5977 case ' ':
5978 case ' ':
5979 /* Just ignore spaces and tabs */
5980 break;
5981 default:
5982 /* Unknown character */
5983 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5984 }
5985 /* Jump out on DTMF */
5986 if (res) {
5987 break;
5988 }
5989 }
5990 return res;
5991}
5992
5993/*! \brief Italian syntax */
5994int 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)
5995{
5996 struct timeval when = { t, 0 };
5997 struct ast_tm tm;
5998 int res=0, offset, sndoffset;
5999 char sndfile[256], nextmsg[256];
6000
6001 if (format == NULL)
6002 format = "AdB 'digits/at' IMp";
6003
6004 ast_localtime(&when, &tm, tzone);
6005
6006 for (offset=0 ; format[offset] != '\0' ; offset++) {
6007 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6008 switch (format[offset]) {
6009 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6010 case '\'':
6011 /* Literal name of a sound file */
6012 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6013 sndfile[sndoffset] = format[offset];
6014 }
6015 sndfile[sndoffset] = '\0';
6016 res = wait_file(chan, ints, sndfile, lang);
6017 break;
6018 case 'A':
6019 case 'a':
6020 /* Sunday - Saturday */
6021 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6022 res = wait_file(chan, ints, nextmsg, lang);
6023 break;
6024 case 'B':
6025 case 'b':
6026 case 'h':
6027 /* January - December */
6028 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6029 res = wait_file(chan, ints, nextmsg, lang);
6030 break;
6031 case 'm':
6032 /* First - Twelfth */
6033 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6034 res = wait_file(chan, ints, nextmsg, lang);
6035 break;
6036 case 'd':
6037 case 'e':
6038 /* First day of the month is spelled as ordinal */
6039 if (tm.tm_mday == 1) {
6040 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
6041 res = wait_file(chan, ints, nextmsg, lang);
6042 } else {
6043 if (!res) {
6044 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6045 }
6046 }
6047 break;
6048 case 'Y':
6049 /* Year */
6050 if (tm.tm_year > 99) {
6051 res = wait_file(chan, ints, "digits/ore-2000", lang);
6052 if (tm.tm_year > 100) {
6053 if (!res) {
6054 /* This works until the end of 2021 */
6055 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
6056 res = wait_file(chan, ints, nextmsg, lang);
6057 }
6058 }
6059 } else {
6060 if (tm.tm_year < 1) {
6061 /* I'm not going to handle 1900 and prior */
6062 /* We'll just be silent on the year, instead of bombing out. */
6063 } else {
6064 res = wait_file(chan, ints, "digits/ore-1900", lang);
6065 if ((!res) && (tm.tm_year != 0)) {
6066 if (tm.tm_year <= 21) {
6067 /* 1910 - 1921 */
6068 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6069 res = wait_file(chan, ints, nextmsg, lang);
6070 } else {
6071 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
6072 int ten, one;
6073 ten = tm.tm_year / 10;
6074 one = tm.tm_year % 10;
6075 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6076 res = wait_file(chan, ints, nextmsg, lang);
6077 if (!res) {
6078 if (one != 0) {
6079 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6080 res = wait_file(chan, ints, nextmsg, lang);
6081 }
6082 }
6083 }
6084 }
6085 }
6086 }
6087 break;
6088 case 'I':
6089 case 'l':
6090 /* 12-Hour */
6091 if (tm.tm_hour == 0) {
6092 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6093 } else if (tm.tm_hour > 12) {
6094 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6095 } else {
6096 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6097 }
6098 res = wait_file(chan, ints, nextmsg, lang);
6099 break;
6100 case 'H':
6101 case 'k':
6102 /* 24-Hour */
6103 if (tm.tm_hour == 0) {
6104 res = wait_file(chan, ints, "digits/ore-mezzanotte", lang);
6105 } else if (tm.tm_hour == 1) {
6106 res = wait_file(chan, ints, "digits/ore-una", lang);
6107 } else {
6108 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6109 }
6110 break;
6111 case 'M':
6112 /* Minute */
6113 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6114 break;
6115 case 'P':
6116 case 'p':
6117 /* AM/PM */
6118 if (tm.tm_hour > 11) {
6119 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6120 } else {
6121 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6122 }
6123 res = wait_file(chan, ints, nextmsg, lang);
6124 break;
6125 case 'Q':
6126 /* Shorthand for "Today", "Yesterday", or ABdY */
6127 /* XXX As emphasized elsewhere, this should the native way in your
6128 * language to say the date, with changes in what you say, depending
6129 * upon how recent the date is. XXX */
6130 {
6131 struct timeval now = ast_tvnow();
6132 struct ast_tm tmnow;
6133 time_t beg_today;
6134
6135 ast_localtime(&now, &tmnow, tzone);
6136 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6137 /* In any case, it saves not having to do ast_mktime() */
6138 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6139 if (beg_today < t) {
6140 /* Today */
6141 res = wait_file(chan, ints, "digits/today", lang);
6142 } else if (beg_today - 86400 < t) {
6143 /* Yesterday */
6144 res = wait_file(chan, ints, "digits/yesterday", lang);
6145 } else {
6146 res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6147 }
6148 }
6149 break;
6150 case 'q':
6151 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6152 {
6153 struct timeval now = ast_tvnow();
6154 struct ast_tm tmnow;
6155 time_t beg_today;
6156
6157 ast_localtime(&now, &tmnow, tzone);
6158 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6159 /* In any case, it saves not having to do ast_mktime() */
6160 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6161 if (beg_today < t) {
6162 /* Today */
6163 } else if ((beg_today - 86400) < t) {
6164 /* Yesterday */
6165 res = wait_file(chan, ints, "digits/yesterday", lang);
6166 } else if (beg_today - 86400 * 6 < t) {
6167 /* Within the last week */
6168 res = ast_say_date_with_format_it(chan, t, ints, lang, "A", tzone);
6169 } else {
6170 res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6171 }
6172 }
6173 break;
6174 case 'R':
6175 res = ast_say_date_with_format_it(chan, t, ints, lang, "HM", tzone);
6176 break;
6177 case 'S':
6178 /* Seconds */
6179 if (tm.tm_sec == 0) {
6180 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6181 res = wait_file(chan, ints, nextmsg, lang);
6182 } else if (tm.tm_sec < 10) {
6183 res = wait_file(chan, ints, "digits/oh", lang);
6184 if (!res) {
6185 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6186 res = wait_file(chan, ints, nextmsg, lang);
6187 }
6188 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6189 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6190 res = wait_file(chan, ints, nextmsg, lang);
6191 } else {
6192 int ten, one;
6193 ten = (tm.tm_sec / 10) * 10;
6194 one = (tm.tm_sec % 10);
6195 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6196 res = wait_file(chan, ints, nextmsg, lang);
6197 if (!res) {
6198 /* Fifty, not fifty-zero */
6199 if (one != 0) {
6200 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6201 res = wait_file(chan, ints, nextmsg, lang);
6202 }
6203 }
6204 }
6205 break;
6206 case 'T':
6207 res = ast_say_date_with_format_it(chan, t, ints, lang, "HMS", tzone);
6208 break;
6209 case ' ':
6210 case ' ':
6211 /* Just ignore spaces and tabs */
6212 break;
6213 default:
6214 /* Unknown character */
6215 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6216 }
6217 /* Jump out on DTMF */
6218 if (res) {
6219 break;
6220 }
6221 }
6222 return res;
6223}
6224
6225/*! \brief Dutch syntax */
6226int 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)
6227{
6228 struct timeval when = { t, 0 };
6229 struct ast_tm tm;
6230 int res=0, offset, sndoffset;
6231 char sndfile[256], nextmsg[256];
6232
6233 if (format == NULL)
6234 format = "AdBY 'digits/at' IMp";
6235
6236 ast_localtime(&when, &tm, tzone);
6237
6238 for (offset=0 ; format[offset] != '\0' ; offset++) {
6239 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6240 switch (format[offset]) {
6241 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6242 case '\'':
6243 /* Literal name of a sound file */
6244 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6245 sndfile[sndoffset] = format[offset];
6246 }
6247 sndfile[sndoffset] = '\0';
6248 res = wait_file(chan, ints, sndfile, lang);
6249 break;
6250 case 'A':
6251 case 'a':
6252 /* Sunday - Saturday */
6253 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6254 res = wait_file(chan, ints, nextmsg, lang);
6255 break;
6256 case 'B':
6257 case 'b':
6258 case 'h':
6259 /* January - December */
6260 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6261 res = wait_file(chan, ints, nextmsg, lang);
6262 break;
6263 case 'm':
6264 /* First - Twelfth */
6265 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6266 res = wait_file(chan, ints, nextmsg, lang);
6267 break;
6268 case 'd':
6269 case 'e':
6270 /* First - Thirtyfirst */
6271 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
6272 break;
6273 case 'Y':
6274 /* Year */
6275 if (tm.tm_year > 99) {
6276 res = wait_file(chan, ints, "digits/2", lang);
6277 if (!res) {
6278 res = wait_file(chan, ints, "digits/thousand", lang);
6279 }
6280 if (tm.tm_year > 100) {
6281 if (!res) {
6282 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char *) NULL);
6283 }
6284 }
6285 } else {
6286 if (tm.tm_year < 1) {
6287 /* I'm not going to handle 1900 and prior */
6288 /* We'll just be silent on the year, instead of bombing out. */
6289 } else {
6290 res = wait_file(chan, ints, "digits/19", lang);
6291 if (!res) {
6292 if (tm.tm_year <= 9) {
6293 /* 1901 - 1909 */
6294 res = wait_file(chan, ints, "digits/oh", lang);
6295 if (!res) {
6296 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6297 res = wait_file(chan, ints, nextmsg, lang);
6298 }
6299 } else if (tm.tm_year <= 20) {
6300 /* 1910 - 1920 */
6301 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6302 res = wait_file(chan, ints, nextmsg, lang);
6303 } else {
6304 /* 1921 - 1999 */
6305 int ten, one;
6306 ten = tm.tm_year / 10;
6307 one = tm.tm_year % 10;
6308 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6309 res = wait_file(chan, ints, nextmsg, lang);
6310 if (!res) {
6311 if (one != 0) {
6312 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6313 res = wait_file(chan, ints, nextmsg, lang);
6314 }
6315 }
6316 }
6317 }
6318 }
6319 }
6320 break;
6321 case 'I':
6322 case 'l':
6323 /* 12-Hour */
6324 if (tm.tm_hour == 0)
6325 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6326 else if (tm.tm_hour > 12)
6327 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6328 else
6329 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6330 res = wait_file(chan, ints, nextmsg, lang);
6331 break;
6332 case 'H':
6333 case 'k':
6334 /* 24-Hour */
6335 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6336 if (!res) {
6337 res = wait_file(chan, ints, "digits/nl-uur", lang);
6338 }
6339 break;
6340 case 'M':
6341 /* Minute */
6342 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6343 break;
6344 case 'P':
6345 case 'p':
6346 /* AM/PM */
6347 if (tm.tm_hour > 11)
6348 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6349 else
6350 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6351 res = wait_file(chan, ints, nextmsg, lang);
6352 break;
6353 case 'Q':
6354 /* Shorthand for "Today", "Yesterday", or AdBY */
6355 /* XXX As emphasized elsewhere, this should the native way in your
6356 * language to say the date, with changes in what you say, depending
6357 * upon how recent the date is. XXX */
6358 {
6359 struct timeval now = ast_tvnow();
6360 struct ast_tm tmnow;
6361 time_t beg_today;
6362
6363 ast_localtime(&now, &tmnow, tzone);
6364 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6365 /* In any case, it saves not having to do ast_mktime() */
6366 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6367 if (beg_today < t) {
6368 /* Today */
6369 res = wait_file(chan, ints, "digits/today", lang);
6370 } else if (beg_today - 86400 < t) {
6371 /* Yesterday */
6372 res = wait_file(chan, ints, "digits/yesterday", lang);
6373 } else {
6374 res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6375 }
6376 }
6377 break;
6378 case 'q':
6379 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6380 {
6381 struct timeval now = ast_tvnow();
6382 struct ast_tm tmnow;
6383 time_t beg_today;
6384
6385 ast_localtime(&now, &tmnow, tzone);
6386 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6387 /* In any case, it saves not having to do ast_mktime() */
6388 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6389 if (beg_today < t) {
6390 /* Today */
6391 } else if ((beg_today - 86400) < t) {
6392 /* Yesterday */
6393 res = wait_file(chan, ints, "digits/yesterday", lang);
6394 } else if (beg_today - 86400 * 6 < t) {
6395 /* Within the last week */
6396 res = ast_say_date_with_format_nl(chan, t, ints, lang, "A", tzone);
6397 } else {
6398 res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6399 }
6400 }
6401 break;
6402 case 'R':
6403 res = ast_say_date_with_format_nl(chan, t, ints, lang, "HM", tzone);
6404 break;
6405 case 'S':
6406 /* Seconds */
6407 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
6408 break;
6409 case 'T':
6410 res = ast_say_date_with_format_nl(chan, t, ints, lang, "HMS", tzone);
6411 break;
6412 case ' ':
6413 case ' ':
6414 /* Just ignore spaces and tabs */
6415 break;
6416 default:
6417 /* Unknown character */
6418 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6419 }
6420 /* Jump out on DTMF */
6421 if (res) {
6422 break;
6423 }
6424 }
6425 return res;
6426}
6427
6428/*! \brief Polish syntax */
6429int 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)
6430{
6431 struct timeval when = { thetime, 0 };
6432 struct ast_tm tm;
6433 int res=0, offset, sndoffset;
6434 char sndfile[256], nextmsg[256];
6435
6436 ast_localtime(&when, &tm, tzone);
6437
6438 for (offset = 0 ; format[offset] != '\0' ; offset++) {
6439 int remaining;
6440 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6441 switch (format[offset]) {
6442 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6443 case '\'':
6444 /* Literal name of a sound file */
6445 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6446 sndfile[sndoffset] = format[offset];
6447 }
6448 sndfile[sndoffset] = '\0';
6449 res = wait_file(chan, ints, sndfile, lang);
6450 break;
6451 case 'A':
6452 case 'a':
6453 /* Sunday - Saturday */
6454 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6455 res = wait_file(chan, ints, nextmsg, lang);
6456 break;
6457 case 'B':
6458 case 'b':
6459 case 'h':
6460 /* January - December */
6461 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6462 res = wait_file(chan, ints, nextmsg, lang);
6463 break;
6464 case 'm':
6465 /* Month enumerated */
6466 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
6467 break;
6468 case 'd':
6469 case 'e':
6470 /* First - Thirtyfirst */
6471 remaining = tm.tm_mday;
6472 if (tm.tm_mday > 30) {
6473 res = wait_file(chan, ints, "digits/h-30", lang);
6474 remaining -= 30;
6475 }
6476 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
6477 res = wait_file(chan, ints, "digits/h-20", lang);
6478 remaining -= 20;
6479 }
6480 if (!res) {
6481 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remaining);
6482 res = wait_file(chan, ints, nextmsg, lang);
6483 }
6484 break;
6485 case 'Y':
6486 /* Year */
6487 if (tm.tm_year > 100) {
6488 res = wait_file(chan, ints, "digits/2", lang);
6489 if (!res)
6490 res = wait_file(chan, ints, "digits/1000.2", lang);
6491 if (tm.tm_year > 100) {
6492 if (!res)
6493 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
6494 }
6495 } else if (tm.tm_year == 100) {
6496 res = wait_file(chan, ints, "digits/h-2000", lang);
6497 } else {
6498 if (tm.tm_year < 1) {
6499 /* I'm not going to handle 1900 and prior */
6500 /* We'll just be silent on the year, instead of bombing out. */
6501 break;
6502 } else {
6503 res = wait_file(chan, ints, "digits/1000", lang);
6504 if (!res) {
6505 wait_file(chan, ints, "digits/900", lang);
6506 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
6507 }
6508 }
6509 }
6510 if (!res)
6511 wait_file(chan, ints, "digits/year", lang);
6512 break;
6513 case 'I':
6514 case 'l':
6515 /* 12-Hour */
6516 if (tm.tm_hour == 0)
6517 ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
6518 else if (tm.tm_hour > 12)
6519 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
6520 else
6521 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6522
6523 res = wait_file(chan, ints, nextmsg, lang);
6524 break;
6525 case 'H':
6526 case 'k':
6527 /* 24-Hour */
6528 if (tm.tm_hour != 0) {
6529 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6530 res = wait_file(chan, ints, nextmsg, lang);
6531 } else
6532 res = wait_file(chan, ints, "digits/t-24", lang);
6533 break;
6534 case 'M':
6535 case 'N':
6536 /* Minute */
6537 if (tm.tm_min == 0) {
6538 if (format[offset] == 'M') {
6539 res = wait_file(chan, ints, "digits/oclock", lang);
6540 } else {
6541 res = wait_file(chan, ints, "digits/100", lang);
6542 }
6543 } else
6544 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6545 break;
6546 case 'P':
6547 case 'p':
6548 /* AM/PM */
6549 if (tm.tm_hour > 11)
6550 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6551 else
6552 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6553 res = wait_file(chan, ints, nextmsg, lang);
6554 break;
6555 case 'Q':
6556 /* Shorthand for "Today", "Yesterday", or AdBY */
6557 {
6558 struct timeval now = ast_tvnow();
6559 struct ast_tm tmnow;
6560 time_t beg_today;
6561
6562 ast_localtime(&now, &tmnow, tzone);
6563 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6564 /* In any case, it saves not having to do ast_mktime() */
6565 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6566 if (beg_today < thetime) {
6567 /* Today */
6568 res = wait_file(chan, ints, "digits/today", lang);
6569 } else if (beg_today - 86400 < thetime) {
6570 /* Yesterday */
6571 res = wait_file(chan, ints, "digits/yesterday", lang);
6572 } else {
6573 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
6574 }
6575 }
6576 break;
6577 case 'q':
6578 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6579 {
6580 struct timeval now = ast_tvnow();
6581 struct ast_tm tmnow;
6582 time_t beg_today;
6583
6584 ast_localtime(&now, &tmnow, tzone);
6585 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6586 /* In any case, it saves not having to do ast_mktime() */
6587 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6588 if (beg_today < thetime) {
6589 /* Today */
6590 } else if ((beg_today - 86400) < thetime) {
6591 /* Yesterday */
6592 res = wait_file(chan, ints, "digits/yesterday", lang);
6593 } else if (beg_today - 86400 * 6 < thetime) {
6594 /* Within the last week */
6595 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", tzone);
6596 } else {
6597 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
6598 }
6599 }
6600 break;
6601 case 'R':
6602 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", tzone);
6603 break;
6604 case 'S':
6605 /* Seconds */
6606 res = wait_file(chan, ints, "digits/and", lang);
6607 if (!res) {
6608 if (tm.tm_sec == 1) {
6609 res = wait_file(chan, ints, "digits/1z", lang);
6610 if (!res)
6611 res = wait_file(chan, ints, "digits/second-a", lang);
6612 } else {
6613 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
6614 if (!res) {
6615 int ten, one;
6616 ten = tm.tm_sec / 10;
6617 one = tm.tm_sec % 10;
6618
6619 if (one > 1 && one < 5 && ten != 1)
6620 res = wait_file(chan, ints, "seconds", lang);
6621 else
6622 res = wait_file(chan, ints, "second", lang);
6623 }
6624 }
6625 }
6626 break;
6627 case 'T':
6628 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", tzone);
6629 break;
6630 case ' ':
6631 case ' ':
6632 /* Just ignore spaces and tabs */
6633 break;
6634 default:
6635 /* Unknown character */
6636 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6637 }
6638 /* Jump out on DTMF */
6639 if (res)
6640 break;
6641 }
6642 return res;
6643}
6644
6645/*! \brief Portuguese syntax */
6646int 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)
6647{
6648 struct timeval when = { t, 0 };
6649 struct ast_tm tm;
6650 int res=0, offset, sndoffset;
6651 char sndfile[256], nextmsg[256];
6652
6653 if (format == NULL)
6654 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
6655
6656 ast_localtime(&when, &tm, tzone);
6657
6658 for (offset=0 ; format[offset] != '\0' ; offset++) {
6659 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6660 switch (format[offset]) {
6661 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6662 case '\'':
6663 /* Literal name of a sound file */
6664 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6665 sndfile[sndoffset] = format[offset];
6666 }
6667 sndfile[sndoffset] = '\0';
6668 snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
6669 res = wait_file(chan, ints, nextmsg, lang);
6670 break;
6671 case 'A':
6672 case 'a':
6673 /* Sunday - Saturday */
6674 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6675 res = wait_file(chan, ints, nextmsg, lang);
6676 break;
6677 case 'B':
6678 case 'b':
6679 case 'h':
6680 /* January - December */
6681 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6682 res = wait_file(chan, ints, nextmsg, lang);
6683 break;
6684 case 'm':
6685 /* First - Twelfth */
6686 if (!strcasecmp(lang, "pt_BR")) {
6687 res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
6688 } else {
6689 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6690 res = wait_file(chan, ints, nextmsg, lang);
6691 }
6692 break;
6693 case 'd':
6694 case 'e':
6695 /* First - Thirtyfirst */
6696 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6697 break;
6698 case 'Y':
6699 /* Year */
6700 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6701 break;
6702 case 'I':
6703 case 'l':
6704 /* 12-Hour */
6705 if (!strcasecmp(lang, "pt_BR")) {
6706 if (tm.tm_hour == 0) {
6707 if (format[offset] == 'I')
6708 res = wait_file(chan, ints, "digits/pt-a", lang);
6709 if (!res)
6710 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
6711 } else if (tm.tm_hour == 12) {
6712 if (format[offset] == 'I')
6713 res = wait_file(chan, ints, "digits/pt-ao", lang);
6714 if (!res)
6715 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
6716 } else {
6717 if (format[offset] == 'I') {
6718 if ((tm.tm_hour % 12) != 1)
6719 res = wait_file(chan, ints, "digits/pt-as", lang);
6720 else
6721 res = wait_file(chan, ints, "digits/pt-a", lang);
6722 }
6723 if (!res)
6724 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
6725 }
6726 } else {
6727 if (tm.tm_hour == 0) {
6728 if (format[offset] == 'I')
6729 res = wait_file(chan, ints, "digits/pt-ah", lang);
6730 if (!res)
6731 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
6732 }
6733 else if (tm.tm_hour == 12) {
6734 if (format[offset] == 'I')
6735 res = wait_file(chan, ints, "digits/pt-ao", lang);
6736 if (!res)
6737 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
6738 }
6739 else {
6740 if (format[offset] == 'I') {
6741 res = wait_file(chan, ints, "digits/pt-ah", lang);
6742 if ((tm.tm_hour % 12) != 1)
6743 if (!res)
6744 res = wait_file(chan, ints, "digits/pt-sss", lang);
6745 }
6746 if (!res)
6747 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
6748 }
6749 }
6750 break;
6751 case 'H':
6752 case 'k':
6753 /* 24-Hour */
6754 if (!strcasecmp(lang, "pt_BR")) {
6755 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
6756 if ((!res) && (format[offset] == 'H')) {
6757 if (tm.tm_hour > 1) {
6758 res = wait_file(chan, ints, "digits/hours", lang);
6759 } else {
6760 res = wait_file(chan, ints, "digits/hour", lang);
6761 }
6762 }
6763 } else {
6764 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
6765 if (!res) {
6766 if (tm.tm_hour != 0) {
6767 int remaining = tm.tm_hour;
6768 if (tm.tm_hour > 20) {
6769 res = wait_file(chan, ints, "digits/20", lang);
6770 remaining -= 20;
6771 }
6772 if (!res) {
6773 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
6774 res = wait_file(chan, ints, nextmsg, lang);
6775 }
6776 }
6777 }
6778 }
6779 break;
6780 case 'M':
6781 /* Minute */
6782 if (!strcasecmp(lang, "pt_BR")) {
6783 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
6784 if (!res) {
6785 if (tm.tm_min > 1) {
6786 res = wait_file(chan, ints, "minutes", lang);
6787 } else {
6788 res = wait_file(chan, ints, "minute", lang);
6789 }
6790 }
6791 } else {
6792 if (tm.tm_min == 0) {
6793 res = wait_file(chan, ints, "digits/pt-hora", lang);
6794 if (tm.tm_hour != 1)
6795 if (!res)
6796 res = wait_file(chan, ints, "digits/pt-sss", lang);
6797 } else {
6798 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6799 }
6800 }
6801 break;
6802 case 'P':
6803 case 'p':
6804 /* AM/PM */
6805 if (!strcasecmp(lang, "pt_BR")) {
6806 if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
6807 res = wait_file(chan, ints, "digits/pt-da", lang);
6808 if (!res) {
6809 if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
6810 res = wait_file(chan, ints, "digits/morning", lang);
6811 else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
6812 res = wait_file(chan, ints, "digits/afternoon", lang);
6813 else res = wait_file(chan, ints, "digits/night", lang);
6814 }
6815 }
6816 } else {
6817 if (tm.tm_hour > 12)
6818 res = wait_file(chan, ints, "digits/p-m", lang);
6819 else if (tm.tm_hour && tm.tm_hour < 12)
6820 res = wait_file(chan, ints, "digits/a-m", lang);
6821 }
6822 break;
6823 case 'Q':
6824 /* Shorthand for "Today", "Yesterday", or ABdY */
6825 /* XXX As emphasized elsewhere, this should the native way in your
6826 * language to say the date, with changes in what you say, depending
6827 * upon how recent the date is. XXX */
6828 {
6829 struct timeval now = ast_tvnow();
6830 struct ast_tm tmnow;
6831 time_t beg_today;
6832
6833 ast_localtime(&now, &tmnow, tzone);
6834 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6835 /* In any case, it saves not having to do ast_mktime() */
6836 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6837 if (beg_today < t) {
6838 /* Today */
6839 res = wait_file(chan, ints, "digits/today", lang);
6840 } else if (beg_today - 86400 < t) {
6841 /* Yesterday */
6842 res = wait_file(chan, ints, "digits/yesterday", lang);
6843 } else {
6844 res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
6845 }
6846 }
6847 break;
6848 case 'q':
6849 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6850 /* XXX As emphasized elsewhere, this should the native way in your
6851 * language to say the date, with changes in what you say, depending
6852 * upon how recent the date is. XXX */
6853 {
6854 struct timeval now = ast_tvnow();
6855 struct ast_tm tmnow;
6856 time_t beg_today;
6857
6858 ast_localtime(&now, &tmnow, tzone);
6859 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6860 /* In any case, it saves not having to do ast_mktime() */
6861 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6862 if (beg_today < t) {
6863 /* Today */
6864 } else if ((beg_today - 86400) < t) {
6865 /* Yesterday */
6866 res = wait_file(chan, ints, "digits/yesterday", lang);
6867 } else if (beg_today - 86400 * 6 < t) {
6868 /* Within the last week */
6869 res = ast_say_date_with_format_pt(chan, t, ints, lang, "A", tzone);
6870 } else {
6871 res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
6872 }
6873 }
6874 break;
6875 case 'R':
6876 res = ast_say_date_with_format_pt(chan, t, ints, lang, "H 'digits/pt-e' M", tzone);
6877 break;
6878 case 'S':
6879 /* Seconds */
6880 if (!strcasecmp(lang, "pt_BR")) {
6881 res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
6882 if (!res) {
6883 if (tm.tm_sec > 1) {
6884 res = wait_file(chan, ints, "seconds", lang);
6885 } else {
6886 res = wait_file(chan, ints, "second", lang);
6887 }
6888 }
6889 } else {
6890 if (tm.tm_sec == 0) {
6891 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6892 res = wait_file(chan, ints, nextmsg, lang);
6893 } else if (tm.tm_sec < 10) {
6894 res = wait_file(chan, ints, "digits/oh", lang);
6895 if (!res) {
6896 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6897 res = wait_file(chan, ints, nextmsg, lang);
6898 }
6899 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6900 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6901 res = wait_file(chan, ints, nextmsg, lang);
6902 } else {
6903 int ten, one;
6904 ten = (tm.tm_sec / 10) * 10;
6905 one = (tm.tm_sec % 10);
6906 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6907 res = wait_file(chan, ints, nextmsg, lang);
6908 if (!res) {
6909 /* Fifty, not fifty-zero */
6910 if (one != 0) {
6911 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6912 res = wait_file(chan, ints, nextmsg, lang);
6913 }
6914 }
6915 }
6916 }
6917 break;
6918 case 'T':
6919 res = ast_say_date_with_format_pt(chan, t, ints, lang, "HMS", tzone);
6920 break;
6921 case ' ':
6922 case ' ':
6923 /* Just ignore spaces and tabs */
6924 break;
6925 default:
6926 /* Unknown character */
6927 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6928 }
6929 /* Jump out on DTMF */
6930 if (res) {
6931 break;
6932 }
6933 }
6934 return res;
6935}
6936
6937/*! \brief Taiwanese / Chinese syntax */
6938int 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)
6939{
6940 struct timeval when = { t, 0 };
6941 struct ast_tm tm;
6942 int res=0, offset, sndoffset;
6943 char sndfile[256], nextmsg[256];
6944
6945 if (format == NULL)
6946 format = "YBdAkM";
6947
6948 ast_localtime(&when, &tm, tzone);
6949
6950 for (offset=0 ; format[offset] != '\0' ; offset++) {
6951 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6952 switch (format[offset]) {
6953 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6954 case '\'':
6955 /* Literal name of a sound file */
6956 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6957 sndfile[sndoffset] = format[offset];
6958 }
6959 sndfile[sndoffset] = '\0';
6960 res = wait_file(chan, ints, sndfile, lang);
6961 break;
6962 case 'A':
6963 case 'a':
6964 /* Sunday - Saturday */
6965 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6966 res = wait_file(chan, ints, nextmsg, lang);
6967 break;
6968 case 'B':
6969 case 'b':
6970 case 'h':
6971 case 'm':
6972 /* January - December */
6973 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6974 res = wait_file(chan, ints, nextmsg, lang);
6975 break;
6976 case 'd':
6977 case 'e':
6978 /* First - Thirtyfirst */
6979 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
6980 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday);
6981 res = wait_file(chan, ints, nextmsg, lang);
6982 } else {
6983 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
6984 res = wait_file(chan, ints, nextmsg, lang);
6985 if (!res) {
6986 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
6987 res = wait_file(chan, ints, nextmsg, lang);
6988 }
6989 }
6990 if (!res) res = wait_file(chan, ints, "digits/day", lang);
6991 break;
6992 case 'Y':
6993 /* Year */
6994 if (tm.tm_year > 99) {
6995 res = wait_file(chan, ints, "digits/2", lang);
6996 if (!res) {
6997 res = wait_file(chan, ints, "digits/thousand", lang);
6998 }
6999 if (tm.tm_year > 100) {
7000 if (!res) {
7001 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
7002 res = wait_file(chan, ints, nextmsg, lang);
7003 if (!res) {
7004 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
7005 res = wait_file(chan, ints, nextmsg, lang);
7006 }
7007 }
7008 }
7009 if (!res) {
7010 res = wait_file(chan, ints, "digits/year", lang);
7011 }
7012 } else {
7013 if (tm.tm_year < 1) {
7014 /* I'm not going to handle 1900 and prior */
7015 /* We'll just be silent on the year, instead of bombing out. */
7016 } else {
7017 res = wait_file(chan, ints, "digits/1", lang);
7018 if (!res) {
7019 res = wait_file(chan, ints, "digits/9", lang);
7020 }
7021 if (!res) {
7022 if (tm.tm_year <= 9) {
7023 /* 1901 - 1909 */
7024 res = wait_file(chan, ints, "digits/0", lang);
7025 if (!res) {
7026 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
7027 res = wait_file(chan, ints, nextmsg, lang);
7028 }
7029 } else {
7030 /* 1910 - 1999 */
7031 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
7032 res = wait_file(chan, ints, nextmsg, lang);
7033 if (!res) {
7034 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
7035 res = wait_file(chan, ints, nextmsg, lang);
7036 }
7037 }
7038 }
7039 }
7040 if (!res) {
7041 res = wait_file(chan, ints, "digits/year", lang);
7042 }
7043 }
7044 break;
7045 case 'I':
7046 case 'l':
7047 /* 12-Hour */
7048 if (tm.tm_hour == 0)
7049 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
7050 else if (tm.tm_hour > 12)
7051 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
7052 else
7053 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
7054 res = wait_file(chan, ints, nextmsg, lang);
7055 if (!res) {
7056 res = wait_file(chan, ints, "digits/oclock", lang);
7057 }
7058 break;
7059 case 'H':
7060 if (tm.tm_hour < 10) {
7061 res = wait_file(chan, ints, "digits/0", lang);
7062 }
7063 /* XXX Static analysis warns of no break here. No idea if this is
7064 * correct or not
7065 */
7066 case 'k':
7067 /* 24-Hour */
7068 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
7069 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
7070 res = wait_file(chan, ints, nextmsg, lang);
7071 } else {
7072 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
7073 res = wait_file(chan, ints, nextmsg, lang);
7074 if (!res) {
7075 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
7076 res = wait_file(chan, ints, nextmsg, lang);
7077 }
7078 }
7079 if (!res) {
7080 res = wait_file(chan, ints, "digits/oclock", lang);
7081 }
7082 break;
7083 case 'M':
7084 /* Minute */
7085 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
7086 if (tm.tm_min < 10) {
7087 res = wait_file(chan, ints, "digits/0", lang);
7088 }
7089 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
7090 res = wait_file(chan, ints, nextmsg, lang);
7091 } else {
7092 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
7093 res = wait_file(chan, ints, nextmsg, lang);
7094 if (!res) {
7095 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
7096 res = wait_file(chan, ints, nextmsg, lang);
7097 }
7098 }
7099 if (!res) {
7100 res = wait_file(chan, ints, "minute", lang);
7101 }
7102 break;
7103 case 'P':
7104 case 'p':
7105 /* AM/PM */
7106 if (tm.tm_hour > 11)
7107 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
7108 else
7109 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
7110 res = wait_file(chan, ints, nextmsg, lang);
7111 break;
7112 case 'Q':
7113 /* Shorthand for "Today", "Yesterday", or ABdY */
7114 /* XXX As emphasized elsewhere, this should the native way in your
7115 * language to say the date, with changes in what you say, depending
7116 * upon how recent the date is. XXX */
7117 {
7118 struct timeval now = ast_tvnow();
7119 struct ast_tm tmnow;
7120 time_t beg_today;
7121
7122 ast_localtime(&now, &tmnow, tzone);
7123 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7124 /* In any case, it saves not having to do ast_mktime() */
7125 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7126 if (beg_today < t) {
7127 /* Today */
7128 res = wait_file(chan, ints, "digits/today", lang);
7129 } else if (beg_today - 86400 < t) {
7130 /* Yesterday */
7131 res = wait_file(chan, ints, "digits/yesterday", lang);
7132 } else {
7133 res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
7134 }
7135 }
7136 break;
7137 case 'q':
7138 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
7139 /* XXX As emphasized elsewhere, this should the native way in your
7140 * language to say the date, with changes in what you say, depending
7141 * upon how recent the date is. XXX */
7142 {
7143 struct timeval now = ast_tvnow();
7144 struct ast_tm tmnow;
7145 time_t beg_today;
7146
7147 ast_localtime(&now, &tmnow, tzone);
7148 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7149 /* In any case, it saves not having to do ast_mktime() */
7150 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7151 if (beg_today < t) {
7152 /* Today */
7153 } else if ((beg_today - 86400) < t) {
7154 /* Yesterday */
7155 res = wait_file(chan, ints, "digits/yesterday", lang);
7156 } else if (beg_today - 86400 * 6 < t) {
7157 /* Within the last week */
7158 res = ast_say_date_with_format_zh(chan, t, ints, lang, "A", tzone);
7159 } else {
7160 res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
7161 }
7162 }
7163 break;
7164 case 'R':
7165 res = ast_say_date_with_format_zh(chan, t, ints, lang, "kM", tzone);
7166 break;
7167 case 'S':
7168 /* Seconds */
7169 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
7170 if (tm.tm_sec < 10) {
7171 res = wait_file(chan, ints, "digits/0", lang);
7172 }
7173 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7174 res = wait_file(chan, ints, nextmsg, lang);
7175 } else {
7176 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
7177 res = wait_file(chan, ints, nextmsg, lang);
7178 if (!res) {
7179 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
7180 res = wait_file(chan, ints, nextmsg, lang);
7181 }
7182 }
7183 if (!res) {
7184 res = wait_file(chan, ints, "second", lang);
7185 }
7186 break;
7187 case 'T':
7188 res = ast_say_date_with_format_zh(chan, t, ints, lang, "HMS", tzone);
7189 break;
7190 case ' ':
7191 case ' ':
7192 /* Just ignore spaces and tabs */
7193 break;
7194 default:
7195 /* Unknown character */
7196 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
7197 }
7198 /* Jump out on DTMF */
7199 if (res) {
7200 break;
7201 }
7202 }
7203 return res;
7204}
7205
7206static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7207{
7208 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7209 return ast_say_time_en(chan, t, ints, lang);
7210 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7211 return ast_say_time_de(chan, t, ints, lang);
7212 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7213 return ast_say_time_fr(chan, t, ints, lang);
7214 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7215 return ast_say_time_gr(chan, t, ints, lang);
7216 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7217 return ast_say_time_ja(chan, t, ints, lang);
7218 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7219 return ast_say_time_he(chan, t, ints, lang);
7220 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7221 return(ast_say_time_hu(chan, t, ints, lang));
7222 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7223 return ast_say_time_ka(chan, t, ints, lang);
7224 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7225 return ast_say_time_nl(chan, t, ints, lang);
7226 } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7227 return ast_say_time_pt_BR(chan, t, ints, lang);
7228 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7229 return ast_say_time_pt(chan, t, ints, lang);
7230 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7231 return(ast_say_time_th(chan, t, ints, lang));
7232 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7233 return ast_say_time_zh(chan, t, ints, lang);
7234 }
7235
7236 /* Default to English */
7237 return ast_say_time_en(chan, t, ints, lang);
7238}
7239
7240/*! \brief English syntax */
7241int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7242{
7243 struct timeval when = { t, 0 };
7244 struct ast_tm tm;
7245 int res = 0;
7246 int hour, pm=0;
7247
7248 ast_localtime(&when, &tm, NULL);
7249 hour = tm.tm_hour;
7250 if (!hour)
7251 hour = 12;
7252 else if (hour == 12)
7253 pm = 1;
7254 else if (hour > 12) {
7255 hour -= 12;
7256 pm = 1;
7257 }
7258 if (!res)
7259 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7260
7261 if (tm.tm_min > 9) {
7262 if (!res)
7263 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7264 } else if (tm.tm_min) {
7265 if (!res)
7266 res = ast_streamfile(chan, "digits/oh", lang);
7267 if (!res)
7268 res = ast_waitstream(chan, ints);
7269 if (!res)
7270 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7271 } else {
7272 if (!res)
7273 res = ast_streamfile(chan, "digits/oclock", lang);
7274 if (!res)
7275 res = ast_waitstream(chan, ints);
7276 }
7277 if (pm) {
7278 if (!res)
7279 res = ast_streamfile(chan, "digits/p-m", lang);
7280 } else {
7281 if (!res)
7282 res = ast_streamfile(chan, "digits/a-m", lang);
7283 }
7284 if (!res)
7285 res = ast_waitstream(chan, ints);
7286 return res;
7287}
7288
7289/*! \brief German syntax */
7290int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7291{
7292 struct timeval when = { t, 0 };
7293 struct ast_tm tm;
7294 int res = 0;
7295
7296 ast_localtime(&when, &tm, NULL);
7297 if (!res)
7298 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7299 if (!res)
7300 res = ast_streamfile(chan, "digits/oclock", lang);
7301 if (!res)
7302 res = ast_waitstream(chan, ints);
7303 if (!res)
7304 if (tm.tm_min > 0)
7305 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7306 return res;
7307}
7308
7309/*! \brief Hungarian syntax */
7310int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7311{
7312 struct timeval when = { t, 0 };
7313 struct ast_tm tm;
7314 int res = 0;
7315
7316 ast_localtime(&when, &tm, NULL);
7317 if (!res)
7318 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7319 if (!res)
7320 res = ast_streamfile(chan, "digits/oclock", lang);
7321 if (!res)
7322 res = ast_waitstream(chan, ints);
7323 if (!res)
7324 if (tm.tm_min > 0) {
7325 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7326 if (!res)
7327 res = ast_streamfile(chan, "minute", lang);
7328 }
7329 return res;
7330}
7331
7332/*! \brief French syntax */
7333int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7334{
7335 struct timeval when = { t, 0 };
7336 struct ast_tm tm;
7337 int res = 0;
7338
7339 ast_localtime(&when, &tm, NULL);
7340
7341 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7342 if (!res) {
7343 res = ast_streamfile(chan, "digits/oclock", lang);
7344 }
7345 if (!res) {
7346 res = ast_waitstream(chan, ints);
7347 }
7348 if (tm.tm_min) {
7349 if (!res) {
7350 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7351 }
7352 }
7353 return res;
7354}
7355
7356/*! \brief Dutch syntax */
7357int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7358{
7359 struct timeval when = { t, 0 };
7360 struct ast_tm tm;
7361 int res = 0;
7362
7363 ast_localtime(&when, &tm, NULL);
7364 if (!res)
7365 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
7366 if (!res)
7367 res = ast_streamfile(chan, "digits/nl-uur", lang);
7368 if (!res)
7369 res = ast_waitstream(chan, ints);
7370 if (!res)
7371 if (tm.tm_min > 0)
7372 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
7373 return res;
7374}
7375
7376/*! \brief Portuguese syntax */
7377int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7378{
7379 struct timeval when = { t, 0 };
7380 struct ast_tm tm;
7381 int res = 0;
7382 int hour;
7383
7384 ast_localtime(&when, &tm, NULL);
7385 hour = tm.tm_hour;
7386 if (!res)
7387 res = ast_say_number(chan, hour, ints, lang, "f");
7388 if (tm.tm_min) {
7389 if (!res)
7390 res = wait_file(chan, ints, "digits/pt-e", lang);
7391 if (!res)
7392 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7393 } else {
7394 if (!res)
7395 res = wait_file(chan, ints, "digits/pt-hora", lang);
7396 if (tm.tm_hour != 1)
7397 if (!res)
7398 res = wait_file(chan, ints, "digits/pt-sss", lang);
7399 }
7400 if (!res)
7401 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7402 return res;
7403}
7404
7405/*! \brief Brazilian Portuguese syntax */
7406int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7407{
7408 struct timeval when = { t, 0 };
7409 struct ast_tm tm;
7410 int res = 0;
7411
7412 ast_localtime(&when, &tm, NULL);
7413
7414 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7415 if (!res) {
7416 if (tm.tm_hour > 1)
7417 res = wait_file(chan, ints, "digits/hours", lang);
7418 else
7419 res = wait_file(chan, ints, "digits/hour", lang);
7420 }
7421 if ((!res) && (tm.tm_min)) {
7422 res = wait_file(chan, ints, "digits/pt-e", lang);
7423 if (!res)
7424 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7425 if (!res) {
7426 if (tm.tm_min > 1)
7427 res = wait_file(chan, ints, "minutes", lang);
7428 else
7429 res = wait_file(chan, ints, "minute", lang);
7430 }
7431 }
7432 return res;
7433}
7434
7435/*! \brief Thai syntax */
7436int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7437{
7438 struct timeval when = { t, 0 };
7439 struct ast_tm tm;
7440 int res = 0;
7441 int hour;
7442 ast_localtime(&when, &tm, NULL);
7443 hour = tm.tm_hour;
7444 if (!hour)
7445 hour = 24;
7446 if (!res)
7447 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7448 if (!res)
7449 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7450 return res;
7451}
7452
7453/*! \brief Taiwanese / Chinese syntax */
7454int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7455{
7456 struct timeval when = { t, 0 };
7457 struct ast_tm tm;
7458 int res = 0;
7459 int hour, pm=0;
7460
7461 ast_localtime(&when, &tm, NULL);
7462 hour = tm.tm_hour;
7463 if (!hour)
7464 hour = 12;
7465 else if (hour == 12)
7466 pm = 1;
7467 else if (hour > 12) {
7468 hour -= 12;
7469 pm = 1;
7470 }
7471 if (pm) {
7472 if (!res)
7473 res = ast_streamfile(chan, "digits/p-m", lang);
7474 } else {
7475 if (!res)
7476 res = ast_streamfile(chan, "digits/a-m", lang);
7477 }
7478 if (!res)
7479 res = ast_waitstream(chan, ints);
7480 if (!res)
7481 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7482 if (!res)
7483 res = ast_streamfile(chan, "digits/oclock", lang);
7484 if (!res)
7485 res = ast_waitstream(chan, ints);
7486 if (!res)
7487 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7488 if (!res)
7489 res = ast_streamfile(chan, "minute", lang);
7490 if (!res)
7491 res = ast_waitstream(chan, ints);
7492 return res;
7493}
7494
7495/*! \brief Hebrew syntax */
7496int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7497{
7498 struct timeval when = { t, 0 };
7499 struct ast_tm tm;
7500 int res = 0;
7501 int hour;
7502
7503 ast_localtime(&when, &tm, NULL);
7504 hour = tm.tm_hour;
7505 if (!hour)
7506 hour = 12;
7507
7508 if (!res)
7509 res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
7510
7511 if (tm.tm_min > 9) {
7512 if (!res)
7513 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7514 } else if (tm.tm_min) {
7515 if (!res) { /* say a leading zero if needed */
7516 res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
7517 }
7518 if (!res)
7519 res = ast_waitstream(chan, ints);
7520 if (!res)
7521 res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7522 } else {
7523 if (!res)
7524 res = ast_waitstream(chan, ints);
7525 }
7526 if (!res)
7527 res = ast_waitstream(chan, ints);
7528 return res;
7529}
7530
7531
7532static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7533{
7534 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7535 return ast_say_datetime_en(chan, t, ints, lang);
7536 } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7537 return ast_say_datetime_de(chan, t, ints, lang);
7538 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7539 return ast_say_datetime_fr(chan, t, ints, lang);
7540 } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7541 return ast_say_datetime_gr(chan, t, ints, lang);
7542 } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7543 return ast_say_datetime_ja(chan, t, ints, lang);
7544 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7545 return ast_say_datetime_he(chan, t, ints, lang);
7546 } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7547 return ast_say_datetime_hu(chan, t, ints, lang);
7548 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7549 return ast_say_datetime_ka(chan, t, ints, lang);
7550 } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7551 return ast_say_datetime_nl(chan, t, ints, lang);
7552 } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7553 return ast_say_datetime_pt_BR(chan, t, ints, lang);
7554 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7555 return ast_say_datetime_pt(chan, t, ints, lang);
7556 } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7557 return ast_say_datetime_th(chan, t, ints, lang);
7558 } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7559 return ast_say_datetime_zh(chan, t, ints, lang);
7560 }
7561
7562 /* Default to English */
7563 return ast_say_datetime_en(chan, t, ints, lang);
7564}
7565
7566/*! \brief English syntax */
7567int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7568{
7569 struct timeval when = { t, 0 };
7570 struct ast_tm tm;
7571 char fn[256];
7572 int res = 0;
7573 int hour, pm=0;
7574
7575 ast_localtime(&when, &tm, NULL);
7576 if (!res) {
7577 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7578 res = ast_streamfile(chan, fn, lang);
7579 if (!res)
7580 res = ast_waitstream(chan, ints);
7581 }
7582 if (!res) {
7583 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7584 res = ast_streamfile(chan, fn, lang);
7585 if (!res)
7586 res = ast_waitstream(chan, ints);
7587 }
7588 if (!res)
7589 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7590
7591 hour = tm.tm_hour;
7592 if (!hour)
7593 hour = 12;
7594 else if (hour == 12)
7595 pm = 1;
7596 else if (hour > 12) {
7597 hour -= 12;
7598 pm = 1;
7599 }
7600 if (!res)
7601 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7602
7603 if (tm.tm_min > 9) {
7604 if (!res)
7605 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7606 } else if (tm.tm_min) {
7607 if (!res)
7608 res = ast_streamfile(chan, "digits/oh", lang);
7609 if (!res)
7610 res = ast_waitstream(chan, ints);
7611 if (!res)
7612 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7613 } else {
7614 if (!res)
7615 res = ast_streamfile(chan, "digits/oclock", lang);
7616 if (!res)
7617 res = ast_waitstream(chan, ints);
7618 }
7619 if (pm) {
7620 if (!res)
7621 res = ast_streamfile(chan, "digits/p-m", lang);
7622 } else {
7623 if (!res)
7624 res = ast_streamfile(chan, "digits/a-m", lang);
7625 }
7626 if (!res)
7627 res = ast_waitstream(chan, ints);
7628 if (!res)
7629 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7630 return res;
7631}
7632
7633/*! \brief German syntax */
7634int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7635{
7636 struct timeval when = { t, 0 };
7637 struct ast_tm tm;
7638 int res = 0;
7639
7640 ast_localtime(&when, &tm, NULL);
7641 res = ast_say_date(chan, t, ints, lang);
7642 if (!res)
7643 ast_say_time(chan, t, ints, lang);
7644 return res;
7645
7646}
7647
7648/*! \brief Hungarian syntax */
7649int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7650{
7651 struct timeval when = { t, 0 };
7652 struct ast_tm tm;
7653 int res = 0;
7654
7655 ast_localtime(&when, &tm, NULL);
7656 res = ast_say_date(chan, t, ints, lang);
7657 if (!res)
7658 ast_say_time(chan, t, ints, lang);
7659 return res;
7660}
7661
7662/*! \brief French syntax */
7663int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7664{
7665 struct timeval when = { t, 0 };
7666 struct ast_tm tm;
7667 char fn[256];
7668 int res = 0;
7669
7670 ast_localtime(&when, &tm, NULL);
7671
7672 if (!res)
7673 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7674
7675 if (!res) {
7676 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7677 res = ast_streamfile(chan, fn, lang);
7678 if (!res)
7679 res = ast_waitstream(chan, ints);
7680 }
7681 if (!res) {
7682 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7683 res = ast_streamfile(chan, fn, lang);
7684 if (!res)
7685 res = ast_waitstream(chan, ints);
7686 }
7687
7688 if (!res)
7689 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7690 if (!res)
7691 res = ast_streamfile(chan, "digits/oclock", lang);
7692 if (tm.tm_min > 0) {
7693 if (!res)
7694 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7695 }
7696 if (!res)
7697 res = ast_waitstream(chan, ints);
7698 if (!res)
7699 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7700 return res;
7701}
7702
7703/*! \brief Dutch syntax */
7704int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7705{
7706 struct timeval when = { t, 0 };
7707 struct ast_tm tm;
7708 int res = 0;
7709
7710 ast_localtime(&when, &tm, NULL);
7711 res = ast_say_date(chan, t, ints, lang);
7712 if (!res) {
7713 res = ast_streamfile(chan, "digits/nl-om", lang);
7714 if (!res)
7715 res = ast_waitstream(chan, ints);
7716 }
7717 if (!res)
7718 ast_say_time(chan, t, ints, lang);
7719 return res;
7720}
7721
7722/*! \brief Portuguese syntax */
7723int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7724{
7725 struct timeval when = { t, 0 };
7726 struct ast_tm tm;
7727 char fn[256];
7728 int res = 0;
7729 int hour, pm=0;
7730
7731 ast_localtime(&when, &tm, NULL);
7732 if (!res) {
7733 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7734 res = ast_streamfile(chan, fn, lang);
7735 if (!res)
7736 res = ast_waitstream(chan, ints);
7737 }
7738 if (!res) {
7739 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7740 res = ast_streamfile(chan, fn, lang);
7741 if (!res)
7742 res = ast_waitstream(chan, ints);
7743 }
7744 if (!res)
7745 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7746
7747 hour = tm.tm_hour;
7748 if (!hour)
7749 hour = 12;
7750 else if (hour == 12)
7751 pm = 1;
7752 else if (hour > 12) {
7753 hour -= 12;
7754 pm = 1;
7755 }
7756 if (!res)
7757 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7758
7759 if (tm.tm_min > 9) {
7760 if (!res)
7761 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7762 } else if (tm.tm_min) {
7763 if (!res)
7764 res = ast_streamfile(chan, "digits/oh", lang);
7765 if (!res)
7766 res = ast_waitstream(chan, ints);
7767 if (!res)
7768 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7769 } else {
7770 if (!res)
7771 res = ast_streamfile(chan, "digits/oclock", lang);
7772 if (!res)
7773 res = ast_waitstream(chan, ints);
7774 }
7775 if (pm) {
7776 if (!res)
7777 res = ast_streamfile(chan, "digits/p-m", lang);
7778 } else {
7779 if (!res)
7780 res = ast_streamfile(chan, "digits/a-m", lang);
7781 }
7782 if (!res)
7783 res = ast_waitstream(chan, ints);
7784 if (!res)
7785 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7786 return res;
7787}
7788
7789/*! \brief Brazilian Portuguese syntax */
7790int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7791{
7792 struct timeval when = { t, 0 };
7793 struct ast_tm tm;
7794 int res = 0;
7795
7796 ast_localtime(&when, &tm, NULL);
7797 res = ast_say_date(chan, t, ints, lang);
7798 if (!res)
7799 res = ast_say_time(chan, t, ints, lang);
7800 return res;
7801}
7802
7803/*! \brief Thai syntax */
7804int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7805{
7806 struct timeval when = { t, 0 };
7807 struct ast_tm tm;
7808 char fn[256];
7809 int res = 0;
7810 int hour;
7811 ast_localtime(&when, &tm, NULL);
7812 if (!res) {
7813 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7814 res = ast_streamfile(chan, fn, lang);
7815 if (!res)
7816 res = ast_waitstream(chan, ints);
7817 }
7818 if (!res) {
7819 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7820 res = ast_streamfile(chan, fn, lang);
7821 if (!res)
7822 res = ast_waitstream(chan, ints);
7823 }
7824 if (!res){
7825 ast_copy_string(fn, "digits/posor", sizeof(fn));
7826 res = ast_streamfile(chan, fn, lang);
7827 res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
7828 }
7829 if (!res)
7830 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7831
7832 hour = tm.tm_hour;
7833 if (!hour)
7834 hour = 24;
7835 if (!res){
7836 ast_copy_string(fn, "digits/wela", sizeof(fn));
7837 res = ast_streamfile(chan, fn, lang);
7838 }
7839 if (!res)
7840 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7841 if (!res)
7842 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7843 return res;
7844}
7845
7846/*! \brief Taiwanese / Chinese syntax */
7847int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7848{
7849 struct timeval when = { t, 0 };
7850 struct ast_tm tm;
7851 char fn[256];
7852 int res = 0;
7853 int hour, pm=0;
7854
7855 ast_localtime(&when, &tm, NULL);
7856 if (!res)
7857 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7858 if (!res) {
7859 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7860 res = ast_streamfile(chan, fn, lang);
7861 if (!res)
7862 res = ast_waitstream(chan, ints);
7863 }
7864 if (!res)
7865 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7866 if (!res) {
7867 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7868 res = ast_streamfile(chan, fn, lang);
7869 if (!res)
7870 res = ast_waitstream(chan, ints);
7871 }
7872
7873 hour = tm.tm_hour;
7874 if (!hour)
7875 hour = 12;
7876 else if (hour == 12)
7877 pm = 1;
7878 else if (hour > 12) {
7879 hour -= 12;
7880 pm = 1;
7881 }
7882 if (pm) {
7883 if (!res)
7884 res = ast_streamfile(chan, "digits/p-m", lang);
7885 } else {
7886 if (!res)
7887 res = ast_streamfile(chan, "digits/a-m", lang);
7888 }
7889 if (!res)
7890 res = ast_waitstream(chan, ints);
7891 if (!res)
7892 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7893 if (!res)
7894 res = ast_streamfile(chan, "digits/oclock", lang);
7895 if (!res)
7896 res = ast_waitstream(chan, ints);
7897 if (!res)
7898 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7899 if (!res)
7900 res = ast_streamfile(chan, "minute", lang);
7901 if (!res)
7902 res = ast_waitstream(chan, ints);
7903 return res;
7904}
7905
7906/*! \brief Hebrew syntax */
7907int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7908{
7909 struct timeval when = { t, 0 };
7910 struct ast_tm tm;
7911 char fn[256];
7912 int res = 0;
7913 int hour;
7914
7915 ast_localtime(&when, &tm, NULL);
7916 if (!res) {
7917 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7918 res = ast_streamfile(chan, fn, lang);
7919 if (!res) {
7920 res = ast_waitstream(chan, ints);
7921 }
7922 }
7923 if (!res) {
7924 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7925 res = ast_streamfile(chan, fn, lang);
7926 if (!res) {
7927 res = ast_waitstream(chan, ints);
7928 }
7929 }
7930 if (!res) {
7931 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
7932 }
7933
7934 hour = tm.tm_hour;
7935 if (!hour) {
7936 hour = 12;
7937 }
7938
7939 if (!res) {
7940 res = ast_say_number(chan, hour, ints, lang, "f");
7941 }
7942
7943 if (tm.tm_min > 9) {
7944 if (!res) {
7945 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7946 }
7947 } else if (tm.tm_min) {
7948 if (!res) {
7949 /* say a leading zero if needed */
7950 res = ast_say_number(chan, 0, ints, lang, "f");
7951 }
7952 if (!res) {
7953 res = ast_waitstream(chan, ints);
7954 }
7955 if (!res) {
7956 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7957 }
7958 } else {
7959 if (!res) {
7960 res = ast_waitstream(chan, ints);
7961 }
7962 }
7963 if (!res) {
7964 res = ast_waitstream(chan, ints);
7965 }
7966 if (!res) {
7967 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
7968 }
7969 return res;
7970}
7971
7972static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7973{
7974 if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7975 return ast_say_datetime_from_now_en(chan, t, ints, lang);
7976 } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7977 return ast_say_datetime_from_now_fr(chan, t, ints, lang);
7978 } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7979 return ast_say_datetime_from_now_he(chan, t, ints, lang);
7980 } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7981 return ast_say_datetime_from_now_ka(chan, t, ints, lang);
7982 } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7983 return ast_say_datetime_from_now_pt(chan, t, ints, lang);
7984 }
7985
7986 /* Default to English */
7987 return ast_say_datetime_from_now_en(chan, t, ints, lang);
7988}
7989
7990/*! \brief English syntax */
7991int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7992{
7993 int res=0;
7994 struct timeval nowtv = ast_tvnow(), when = { t, 0 };
7995 int daydiff;
7996 struct ast_tm tm;
7997 struct ast_tm now;
7998 char fn[256];
7999
8000 ast_localtime(&when, &tm, NULL);
8001 ast_localtime(&nowtv, &now, NULL);
8002 daydiff = now.tm_yday - tm.tm_yday;
8003 if ((daydiff < 0) || (daydiff > 6)) {
8004 /* Day of month and month */
8005 if (!res) {
8006 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8007 res = ast_streamfile(chan, fn, lang);
8008 if (!res)
8009 res = ast_waitstream(chan, ints);
8010 }
8011 if (!res)
8012 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8013
8014 } else if (daydiff) {
8015 /* Just what day of the week */
8016 if (!res) {
8017 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8018 res = ast_streamfile(chan, fn, lang);
8019 if (!res)
8020 res = ast_waitstream(chan, ints);
8021 }
8022 } /* Otherwise, it was today */
8023 if (!res)
8024 res = ast_say_time(chan, t, ints, lang);
8025 return res;
8026}
8027
8028/*! \brief French syntax */
8029int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8030{
8031 int res=0;
8032 struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8033 int daydiff;
8034 struct ast_tm tm;
8035 struct ast_tm now;
8036 char fn[256];
8037
8038 ast_localtime(&when, &tm, NULL);
8039 ast_localtime(&nowtv, &now, NULL);
8040 daydiff = now.tm_yday - tm.tm_yday;
8041 if ((daydiff < 0) || (daydiff > 6)) {
8042 /* Day of month and month */
8043 if (!res) {
8044 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8045 res = ast_streamfile(chan, fn, lang);
8046 if (!res)
8047 res = ast_waitstream(chan, ints);
8048 }
8049 if (!res)
8050 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8051
8052 } else if (daydiff) {
8053 /* Just what day of the week */
8054 if (!res) {
8055 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8056 res = ast_streamfile(chan, fn, lang);
8057 if (!res)
8058 res = ast_waitstream(chan, ints);
8059 }
8060 } /* Otherwise, it was today */
8061 if (!res)
8062 res = ast_say_time(chan, t, ints, lang);
8063 return res;
8064}
8065
8066/*! \brief Portuguese syntax */
8067int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8068{
8069 int res=0;
8070 int daydiff;
8071 struct ast_tm tm;
8072 struct ast_tm now;
8073 struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8074 char fn[256];
8075
8076 ast_localtime(&when, &tm, NULL);
8077 ast_localtime(&nowtv, &now, NULL);
8078 daydiff = now.tm_yday - tm.tm_yday;
8079 if ((daydiff < 0) || (daydiff > 6)) {
8080 /* Day of month and month */
8081 if (!res)
8082 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8083 if (!res)
8084 res = wait_file(chan, ints, "digits/pt-de", lang);
8085 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8086 if (!res)
8087 res = wait_file(chan, ints, fn, lang);
8088
8089 } else if (daydiff) {
8090 /* Just what day of the week */
8091 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8092 if (!res)
8093 res = wait_file(chan, ints, fn, lang);
8094 } /* Otherwise, it was today */
8095 if (!strcasecmp(lang, "pt_BR")) {
8096 if (tm.tm_hour > 1) {
8097 ast_copy_string(fn, "digits/pt-as", sizeof(fn));
8098 } else {
8099 ast_copy_string(fn, "digits/pt-a", sizeof(fn));
8100 }
8101 if (!res)
8102 res = wait_file(chan, ints, fn, lang);
8103 } else {
8104 ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
8105 if (!res)
8106 res = wait_file(chan, ints, fn, lang);
8107 if (tm.tm_hour != 1)
8108 if (!res)
8109 res = wait_file(chan, ints, "digits/pt-sss", lang);
8110 if (!res)
8111 res = ast_say_time(chan, t, ints, lang);
8112 }
8113 return res;
8114}
8115
8116/*! \brief Hebrew syntax */
8117int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8118{
8119 int res = 0;
8120 struct timeval nowt = ast_tvnow(), when = { t, 0 };
8121 int daydiff;
8122 struct ast_tm tm;
8123 struct ast_tm now;
8124 char fn[256];
8125
8126 ast_localtime(&when, &tm, NULL);
8127 ast_localtime(&nowt, &now, NULL);
8128 daydiff = now.tm_yday - tm.tm_yday;
8129 if ((daydiff < 0) || (daydiff > 6)) {
8130 /* Day of month and month */
8131 if (!res) {
8132 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8133 res = ast_streamfile(chan, fn, lang);
8134 if (!res)
8135 res = ast_waitstream(chan, ints);
8136 }
8137 if (!res) {
8138 res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
8139 }
8140 } else if (daydiff) {
8141 /* Just what day of the week */
8142 if (!res) {
8143 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8144 res = ast_streamfile(chan, fn, lang);
8145 if (!res) {
8146 res = ast_waitstream(chan, ints);
8147 }
8148 }
8149 } /* Otherwise, it was today */
8150 if (!res) {
8151 res = ast_say_time(chan, t, ints, lang);
8152 }
8153 return res;
8154}
8155
8156
8157
8158/*! \brief Greek
8159 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
8160 */
8161static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
8162 int tmp;
8163 int left;
8164 int res;
8165 char fn[256] = "";
8166
8167 /* ast_debug(1, "\n\n Saying number female %s %d \n\n", lang, num); */
8168 if (num < 5) {
8169 snprintf(fn, sizeof(fn), "digits/female-%d", num);
8170 res = wait_file(chan, ints, fn, lang);
8171 } else if (num < 13) {
8172 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
8173 } else if (num <100 ) {
8174 tmp = (num/10) * 10;
8175 left = num - tmp;
8176 snprintf(fn, sizeof(fn), "digits/%d", tmp);
8177 res = ast_streamfile(chan, fn, lang);
8178 if (!res)
8179 res = ast_waitstream(chan, ints);
8180 if (left)
8181 gr_say_number_female(left, chan, ints, lang);
8182
8183 } else {
8184 return -1;
8185 }
8186 return res;
8187}
8188
8189
8190
8191/*! \brief Greek support
8192 * A list of the files that you need to create
8193 -> digits/xilia = "xilia"
8194 -> digits/myrio = "ekatomyrio"
8195 -> digits/thousands = "xiliades"
8196 -> digits/millions = "ektatomyria"
8197 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
8198 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
8199 e.g. 80 = "ogdonta"
8200 Here we must note that we use digits/tens/100 to utter "ekato"
8201 and digits/hundred-100 to utter "ekaton"
8202 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
8203 "terakosia". Here again we use hundreds/1000 for "xilia"
8204 and digits/thousands for "xiliades"
8205*/
8206static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8207{
8208 int res = 0;
8209 char fn[256] = "";
8210 int i=0;
8211
8212
8213 if (!num) {
8214 ast_copy_string(fn, "digits/0", sizeof(fn));
8215 res = ast_streamfile(chan, fn, ast_channel_language(chan));
8216 if (!res)
8217 return ast_waitstream(chan, ints);
8218 }
8219
8220 while (!res && num ) {
8221 i++;
8222 if (num < 13) {
8223 snprintf(fn, sizeof(fn), "digits/%d", num);
8224 num = 0;
8225 } else if (num <= 100) {
8226 /* 13 < num <= 100 */
8227 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8228 num %= 10;
8229 } else if (num < 200) {
8230 /* 100 < num < 200 */
8231 snprintf(fn, sizeof(fn), "digits/hundred-100");
8232 num %= 100;
8233 } else if (num < 1000) {
8234 /* 200 < num < 1000 */
8235 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
8236 num %= 100;
8237 } else if (num < 2000){
8238 snprintf(fn, sizeof(fn), "digits/xilia");
8239 num %= 1000;
8240 } else {
8241 /* num > 1000 */
8242 if (num < 1000000) {
8243 res = ast_say_number_full_gr(chan, (num / 1000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8244 if (res)
8245 return res;
8246 num %= 1000;
8247 snprintf(fn, sizeof(fn), "digits/thousands");
8248 } else {
8249 if (num < 1000000000) { /* 1,000,000,000 */
8250 res = ast_say_number_full_gr(chan, (num / 1000000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8251 if (res)
8252 return res;
8253 num %= 1000000;
8254 snprintf(fn, sizeof(fn), "digits/millions");
8255 } else {
8256 ast_debug(1, "Number '%d' is too big for me\n", num);
8257 res = -1;
8258 }
8259 }
8260 }
8261 if (!res) {
8262 if (!ast_streamfile(chan, fn, language)) {
8263 if ((audiofd > -1) && (ctrlfd > -1))
8264 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8265 else
8266 res = ast_waitstream(chan, ints);
8267 }
8268 ast_stopstream(chan);
8269 }
8270 }
8271 return res;
8272}
8273
8274/* Japanese syntax */
8275static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8276{
8277 int res = 0;
8278 int playh = 0;
8279 char fn[256] = "";
8280
8281 if (!num)
8282 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
8283
8284 while (!res && (num || playh)) {
8285 if (num < 0) {
8286 ast_copy_string(fn, "digits/minus", sizeof(fn));
8287 if ( num > INT_MIN ) {
8288 num = -num;
8289 } else {
8290 num = 0;
8291 }
8292 } else if (playh) {
8293 ast_copy_string(fn, "digits/hundred", sizeof(fn));
8294 playh = 0;
8295 } else if (num < 20) {
8296 snprintf(fn, sizeof(fn), "digits/%d", num);
8297 num = 0;
8298 } else if (num < 100) {
8299 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8300 num %= 10;
8301 } else {
8302 if (num < 1000){
8303 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
8304 playh++;
8305 num %= 100;
8306 } else {
8307 if (num < 1000000) { /* 1,000,000 */
8308 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
8309 if (res)
8310 return res;
8311 num %= 1000;
8312 snprintf(fn, sizeof(fn), "digits/thousand");
8313 } else {
8314 if (num < 1000000000) { /* 1,000,000,000 */
8315 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
8316 if (res)
8317 return res;
8318 num %= 1000000;
8319 ast_copy_string(fn, "digits/million", sizeof(fn));
8320 } else {
8321 ast_debug(1, "Number '%d' is too big for me\n", num);
8322 res = -1;
8323 }
8324 }
8325 }
8326 }
8327 if (!res) {
8328 if (!ast_streamfile(chan, fn, language)) {
8329 if ((audiofd > -1) && (ctrlfd > -1))
8330 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8331 else
8332 res = ast_waitstream(chan, ints);
8333 }
8334 ast_stopstream(chan);
8335 }
8336 }
8337 return res;
8338}
8339
8340
8341/*! \brief Greek support
8342 *
8343 * The format is weekday - day - month -year
8344 *
8345 * A list of the files that you need to create
8346 * digits/day-[1..7] : "Deytera .. Paraskeyh"
8347 * digits/months/1..12 : "Ianouariou .. Dekembriou"
8348 Attention the months are in "gekinh klhsh"
8349 */
8350static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8351{
8352 struct ast_tm tm;
8353 struct timeval when = { t, 0 };
8354
8355 char fn[256];
8356 int res = 0;
8357
8358
8359 ast_localtime(&when, &tm, NULL);
8360 /* W E E K - D A Y */
8361 if (!res) {
8362 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8363 res = ast_streamfile(chan, fn, lang);
8364 if (!res)
8365 res = ast_waitstream(chan, ints);
8366 }
8367 /* D A Y */
8368 if (!res) {
8369 gr_say_number_female(tm.tm_mday, chan, ints, lang);
8370 }
8371 /* M O N T H */
8372 if (!res) {
8373 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8374 res = ast_streamfile(chan, fn, lang);
8375 if (!res)
8376 res = ast_waitstream(chan, ints);
8377 }
8378 /* Y E A R */
8379 if (!res)
8380 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8381 return res;
8382}
8383
8384
8385/* Japanese syntax */
8386int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8387{
8388 struct ast_tm tm;
8389 struct timeval tv = { t, 0 };
8390 char fn[256];
8391 int res = 0;
8392
8393 ast_localtime(&tv, &tm, NULL);
8394
8395 if (!res)
8396 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8397 if (!res)
8398 res = ast_waitstream(chan, ints);
8399 if (!res)
8400 res = ast_streamfile(chan, "digits/nen", lang);
8401 if (!res) {
8402 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8403 res = ast_streamfile(chan, fn, lang);
8404 if (!res)
8405 res = ast_waitstream(chan, ints);
8406 }
8407 if (!res)
8408 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
8409 if (!res)
8410 res = ast_streamfile(chan, "digits/nichi", lang);
8411 if (!res) {
8412 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8413 res = ast_streamfile(chan, fn, lang);
8414 if (!res)
8415 res = ast_waitstream(chan, ints);
8416 }
8417 return res;
8418}
8419
8420
8421/*! \brief Greek support
8422 *
8423 * A list of the files that you need to create
8424 * - digits/female/1..4 : "Mia, dyo , treis, tesseris "
8425 * - digits/kai : "KAI"
8426 * - digits : "h wra"
8427 * - digits/p-m : "meta meshmbrias"
8428 * - digits/a-m : "pro meshmbrias"
8429 */
8430static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8431{
8432
8433 struct timeval when = { t, 0 };
8434 struct ast_tm tm;
8435 int res = 0;
8436 int hour, pm=0;
8437
8438 ast_localtime(&when, &tm, NULL);
8439 hour = tm.tm_hour;
8440
8441 if (!hour)
8442 hour = 12;
8443 else if (hour == 12)
8444 pm = 1;
8445 else if (hour > 12) {
8446 hour -= 12;
8447 pm = 1;
8448 }
8449
8450 res = gr_say_number_female(hour, chan, ints, lang);
8451 if (tm.tm_min) {
8452 if (!res)
8453 res = ast_streamfile(chan, "digits/kai", lang);
8454 if (!res)
8455 res = ast_waitstream(chan, ints);
8456 if (!res)
8457 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8458 } else {
8459 if (!res)
8460 res = ast_streamfile(chan, "digits/hwra", lang);
8461 if (!res)
8462 res = ast_waitstream(chan, ints);
8463 }
8464 if (pm) {
8465 if (!res)
8466 res = ast_streamfile(chan, "digits/p-m", lang);
8467 } else {
8468 if (!res)
8469 res = ast_streamfile(chan, "digits/a-m", lang);
8470 }
8471 if (!res)
8472 res = ast_waitstream(chan, ints);
8473 return res;
8474}
8475
8476
8477/* Japanese */
8478static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8479{
8480 struct timeval tv = { t, 0 };
8481 struct ast_tm tm;
8482 int res = 0;
8483 int hour, pm=0;
8484
8485 ast_localtime(&tv, &tm, NULL);
8486 hour = tm.tm_hour;
8487
8488 if (!hour)
8489 hour = 12;
8490 else if (hour == 12)
8491 pm = 1;
8492 else if (hour > 12) {
8493 hour -= 12;
8494 pm = 1;
8495 }
8496
8497 if (pm) {
8498 if (!res)
8499 res = ast_streamfile(chan, "digits/p-m", lang);
8500 } else {
8501 if (!res)
8502 res = ast_streamfile(chan, "digits/a-m", lang);
8503 }
8504 if (hour == 9 || hour == 21) {
8505 if (!res)
8506 res = ast_streamfile(chan, "digits/9_2", lang);
8507 } else {
8508 if (!res)
8509 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8510 }
8511 if (!res)
8512 res = ast_streamfile(chan, "digits/ji", lang);
8513 if (!res)
8514 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8515 if (!res)
8516 res = ast_streamfile(chan, "digits/fun", lang);
8517 if (!res)
8518 res = ast_waitstream(chan, ints);
8519 return res;
8520}
8521
8522
8523/*! \brief Greek support
8524 */
8525static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8526{
8527 struct timeval when = { t, 0 };
8528 struct ast_tm tm;
8529 char fn[256];
8530 int res = 0;
8531
8532 ast_localtime(&when, &tm, NULL);
8533
8534 /* W E E K - D A Y */
8535 if (!res) {
8536 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8537 res = ast_streamfile(chan, fn, lang);
8538 if (!res)
8539 res = ast_waitstream(chan, ints);
8540 }
8541 /* D A Y */
8542 if (!res) {
8543 gr_say_number_female(tm.tm_mday, chan, ints, lang);
8544 }
8545 /* M O N T H */
8546 if (!res) {
8547 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8548 res = ast_streamfile(chan, fn, lang);
8549 if (!res)
8550 res = ast_waitstream(chan, ints);
8551 }
8552
8553 res = ast_say_time_gr(chan, t, ints, lang);
8554 return res;
8555}
8556
8557/* Japanese syntax */
8558int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8559{
8560 struct timeval tv = { t, 0 };
8561 struct ast_tm tm;
8562 char fn[256];
8563 int res = 0;
8564 int hour, pm = 0;
8565
8566 ast_localtime(&tv, &tm, NULL);
8567
8568 if (!res)
8569 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8570 if (!res)
8571 res = ast_streamfile(chan, "digits/nen", lang);
8572 if (!res) {
8573 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8574 res = ast_streamfile(chan, fn, lang);
8575 if (!res)
8576 res = ast_waitstream(chan, ints);
8577 }
8578 if (!res)
8579 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8580 if (!res)
8581 res = ast_streamfile(chan, "digits/nichi", lang);
8582 if (!res) {
8583 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8584 res = ast_streamfile(chan, fn, lang);
8585 if (!res)
8586 res = ast_waitstream(chan, ints);
8587 }
8588
8589 hour = tm.tm_hour;
8590 if (!hour)
8591 hour = 12;
8592 else if (hour == 12)
8593 pm = 1;
8594 else if (hour > 12) {
8595 hour -= 12;
8596 pm = 1;
8597 }
8598 if (pm) {
8599 if (!res)
8600 res = ast_streamfile(chan, "digits/p-m", lang);
8601 } else {
8602 if (!res)
8603 res = ast_streamfile(chan, "digits/a-m", lang);
8604 }
8605 if (hour == 9 || hour == 21) {
8606 if (!res)
8607 res = ast_streamfile(chan, "digits/9_2", lang);
8608 } else {
8609 if (!res)
8610 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8611 }
8612 if (!res)
8613 res = ast_streamfile(chan, "digits/ji", lang);
8614 if (!res)
8615 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8616 if (!res)
8617 res = ast_streamfile(chan, "digits/fun", lang);
8618 if (!res)
8619 res = ast_waitstream(chan, ints);
8620 return res;
8621}
8622
8623/*! \brief Greek support
8624 */
8625static 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)
8626{
8627 struct timeval when = { t, 0 };
8628 struct ast_tm tm;
8629 int res=0, offset, sndoffset;
8630 char sndfile[256], nextmsg[256];
8631
8632 if (!format)
8633 format = "AdBY 'digits/at' IMp";
8634
8635 ast_localtime(&when, &tm, tzone);
8636
8637 for (offset=0 ; format[offset] != '\0' ; offset++) {
8638 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8639 switch (format[offset]) {
8640 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8641 case '\'':
8642 /* Literal name of a sound file */
8643 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8644 sndfile[sndoffset] = format[offset];
8645 }
8646 sndfile[sndoffset] = '\0';
8647 res = wait_file(chan, ints, sndfile, lang);
8648 break;
8649 case 'A':
8650 case 'a':
8651 /* Sunday - Saturday */
8652 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8653 res = wait_file(chan, ints, nextmsg, lang);
8654 break;
8655 case 'B':
8656 case 'b':
8657 case 'h':
8658 /* January - December */
8659 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8660 res = wait_file(chan, ints, nextmsg, lang);
8661 break;
8662 case 'd':
8663 case 'e':
8664 /* first - thirtyfirst */
8665 gr_say_number_female(tm.tm_mday, chan, ints, lang);
8666 break;
8667 case 'Y':
8668 /* Year */
8669
8670 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, ast_channel_language(chan), -1, -1);
8671 break;
8672 case 'I':
8673 case 'l':
8674 /* 12-Hour */
8675 if (tm.tm_hour == 0)
8676 gr_say_number_female(12, chan, ints, lang);
8677 else if (tm.tm_hour > 12)
8678 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
8679 else
8680 gr_say_number_female(tm.tm_hour, chan, ints, lang);
8681 break;
8682 case 'H':
8683 case 'k':
8684 /* 24-Hour */
8685 gr_say_number_female(tm.tm_hour, chan, ints, lang);
8686 break;
8687 case 'M':
8688 /* Minute */
8689 if (tm.tm_min) {
8690 if (!res)
8691 res = ast_streamfile(chan, "digits/kai", lang);
8692 if (!res)
8693 res = ast_waitstream(chan, ints);
8694 if (!res)
8695 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
8696 } else {
8697 if (!res)
8698 res = ast_streamfile(chan, "digits/oclock", lang);
8699 if (!res)
8700 res = ast_waitstream(chan, ints);
8701 }
8702 break;
8703 case 'P':
8704 case 'p':
8705 /* AM/PM */
8706 if (tm.tm_hour > 11)
8707 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
8708 else
8709 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
8710 res = wait_file(chan, ints, nextmsg, lang);
8711 break;
8712 case 'Q':
8713 /* Shorthand for "Today", "Yesterday", or ABdY */
8714 /* XXX As emphasized elsewhere, this should the native way in your
8715 * language to say the date, with changes in what you say, depending
8716 * upon how recent the date is. XXX */
8717 {
8718 struct timeval now = ast_tvnow();
8719 struct ast_tm tmnow;
8720 time_t beg_today;
8721
8722 ast_localtime(&now, &tmnow, tzone);
8723 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8724 /* In any case, it saves not having to do ast_mktime() */
8725 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8726 if (beg_today < t) {
8727 /* Today */
8728 res = wait_file(chan, ints, "digits/today", lang);
8729 } else if (beg_today - 86400 < t) {
8730 /* Yesterday */
8731 res = wait_file(chan, ints, "digits/yesterday", lang);
8732 } else {
8733 res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
8734 }
8735 }
8736 break;
8737 case 'q':
8738 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
8739 /* XXX As emphasized elsewhere, this should the native way in your
8740 * language to say the date, with changes in what you say, depending
8741 * upon how recent the date is. XXX */
8742 {
8743 struct timeval now = ast_tvnow();
8744 struct ast_tm tmnow;
8745 time_t beg_today;
8746
8747 ast_localtime(&now, &tmnow, tzone);
8748 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8749 /* In any case, it saves not having to do ast_mktime() */
8750 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8751 if (beg_today < t) {
8752 /* Today */
8753 } else if ((beg_today - 86400) < t) {
8754 /* Yesterday */
8755 res = wait_file(chan, ints, "digits/yesterday", lang);
8756 } else if (beg_today - 86400 * 6 < t) {
8757 /* Within the last week */
8758 res = ast_say_date_with_format_gr(chan, t, ints, lang, "A", tzone);
8759 } else {
8760 res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
8761 }
8762 }
8763 break;
8764 case 'R':
8765 res = ast_say_date_with_format_gr(chan, t, ints, lang, "HM", tzone);
8766 break;
8767 case 'S':
8768 /* Seconds */
8769 ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
8770 res = wait_file(chan, ints, nextmsg, lang);
8771 if (!res)
8772 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
8773 if (!res)
8774 ast_copy_string(nextmsg, "seconds", sizeof(nextmsg));
8775 res = wait_file(chan, ints, nextmsg, lang);
8776 break;
8777 case 'T':
8778 res = ast_say_date_with_format_gr(chan, t, ints, lang, "HMS", tzone);
8779 break;
8780 case ' ':
8781 case ' ':
8782 /* Just ignore spaces and tabs */
8783 break;
8784 default:
8785 /* Unknown character */
8786 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
8787 }
8788 /* Jump out on DTMF */
8789 if (res) {
8790 break;
8791 }
8792 }
8793 return res;
8794}
8795
8796/* Japanese syntax */
8797int 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)
8798{
8799 struct timeval tv = { time, 0 };
8800 struct ast_tm tm;
8801 int res = 0, offset, sndoffset;
8802 char sndfile[256], nextmsg[256];
8803
8804 if (!format)
8805 format = "YbdAPIMS";
8806
8807 ast_localtime(&tv, &tm, timezone);
8808
8809 for (offset = 0; format[offset] != '\0'; offset++) {
8810 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8811 switch (format[offset]) {
8812 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8813 case '\'':
8814 /* Literal name of a sound file */
8815 for (sndoffset = 0; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8816 sndfile[sndoffset] = format[offset];
8817 }
8818 sndfile[sndoffset] = '\0';
8819 res = wait_file(chan,ints,sndfile,lang);
8820 break;
8821 case 'A':
8822 case 'a':
8823 /* Sunday - Saturday */
8824 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8825 res = wait_file(chan,ints,nextmsg,lang);
8826 break;
8827 case 'B':
8828 case 'b':
8829 case 'h':
8830 /* January - December */
8831 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8832 res = wait_file(chan,ints,nextmsg,lang);
8833 break;
8834 case 'd':
8835 case 'e':
8836 /* First - Thirtyfirst */
8837 if (tm.tm_mday < 21) {
8838 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d_2", tm.tm_mday);
8839 res = wait_file(chan,ints,nextmsg,lang);
8840 } else if (tm.tm_mday < 30) {
8841 /* Between 21 and 29 - two sounds */
8842 res = wait_file(chan,ints, "digits/20",lang);
8843 if (!res) {
8844 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
8845 res = wait_file(chan,ints,nextmsg,lang);
8846 }
8847 res = wait_file(chan,ints, "digits/nichi",lang);
8848 } else if (tm.tm_mday == 30) {
8849 /* 30 */
8850 res = wait_file(chan,ints, "digits/h-30_2",lang);
8851 } else {
8852 /* 31 */
8853 res = wait_file(chan,ints, "digits/30",lang);
8854 res = wait_file(chan,ints, "digits/1",lang);
8855 res = wait_file(chan,ints, "digits/nichi",lang);
8856 }
8857 break;
8858 case 'Y':
8859 /* Year */
8860 if (tm.tm_year > 99) {
8861 res = wait_file(chan,ints, "digits/2",lang);
8862 if (!res) {
8863 res = wait_file(chan,ints, "digits/thousand",lang);
8864 }
8865 if (tm.tm_year > 100) {
8866 if (!res) {
8867 /* This works until the end of 2020 */
8868 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
8869 res = wait_file(chan,ints,nextmsg,lang);
8870 }
8871 }
8872 } else {
8873 if (tm.tm_year < 1) {
8874 /* I'm not going to handle 1900 and prior */
8875 /* We'll just be silent on the year, instead of bombing out. */
8876 } else {
8877 res = wait_file(chan,ints, "digits/19",lang);
8878 if (!res) {
8879 if (tm.tm_year <= 9) {
8880 /* 1901 - 1909 */
8881 res = wait_file(chan,ints, "digits/oh",lang);
8882 if (!res) {
8883 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
8884 res = wait_file(chan,ints,nextmsg,lang);
8885 }
8886 } else if (tm.tm_year <= 20) {
8887 /* 1910 - 1920 */
8888 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
8889 res = wait_file(chan,ints,nextmsg,lang);
8890 } else {
8891 /* 1921 - 1999 */
8892 int ten, one;
8893 ten = tm.tm_year / 10;
8894 one = tm.tm_year % 10;
8895 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
8896 res = wait_file(chan,ints,nextmsg,lang);
8897 if (!res) {
8898 if (one != 0) {
8899 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8900 res = wait_file(chan,ints,nextmsg,lang);
8901 }
8902 }
8903 }
8904 }
8905 }
8906 }
8907 res = wait_file(chan,ints, "digits/nen",lang);
8908 break;
8909 case 'P':
8910 case 'p':
8911 /* AM/PM */
8912 if (tm.tm_hour > 11)
8913 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
8914 else
8915 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
8916 res = wait_file(chan,ints,nextmsg,lang);
8917 break;
8918 case 'I':
8919 case 'l':
8920 /* 12-Hour */
8921 if (tm.tm_hour == 0)
8922 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
8923 else if (tm.tm_hour == 9 || tm.tm_hour == 21)
8924 snprintf(nextmsg,sizeof(nextmsg), "digits/9_2");
8925 else if (tm.tm_hour > 12)
8926 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
8927 else
8928 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
8929 res = wait_file(chan,ints,nextmsg,lang);
8930 if(!res) res = wait_file(chan,ints, "digits/ji",lang);
8931 break;
8932 case 'H':
8933 case 'k':
8934 if (!res) {
8935 if (tm.tm_hour != 0) {
8936 int remainder = tm.tm_hour;
8937 if (tm.tm_hour > 20) {
8938 res = wait_file(chan,ints, "digits/20",lang);
8939 remainder -= 20;
8940 }
8941 if (!res) {
8942 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
8943 res = wait_file(chan,ints,nextmsg,lang);
8944 }
8945 }
8946 }
8947 res = wait_file(chan,ints, "digits/ji",lang);
8948 break;
8949 case 'M':
8950 /* Minute */
8951 if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
8952 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
8953 res = wait_file(chan,ints,nextmsg,lang);
8954 } else {
8955 int ten, one;
8956 ten = (tm.tm_min / 10) * 10;
8957 one = (tm.tm_min % 10);
8958 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
8959 res = wait_file(chan,ints,nextmsg,lang);
8960 if (!res) {
8961 /* Fifty, not fifty-zero */
8962 if (one != 0) {
8963 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8964 res = wait_file(chan,ints,nextmsg,lang);
8965 }
8966 }
8967 }
8968 res = wait_file(chan,ints, "digits/fun",lang);
8969 break;
8970 case 'Q':
8971 /* Shorthand for "Today", "Yesterday", or ABdY */
8972 {
8973 struct timeval now;
8974 struct ast_tm tmnow;
8975 time_t beg_today;
8976
8977 gettimeofday(&now,NULL);
8978 ast_localtime(&now,&tmnow,timezone);
8979 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8980 /* In any case, it saves not having to do ast_mktime() */
8981 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8982 if (beg_today < time) {
8983 /* Today */
8984 res = wait_file(chan,ints, "digits/today",lang);
8985 } else if (beg_today - 86400 < time) {
8986 /* Yesterday */
8987 res = wait_file(chan,ints, "digits/yesterday",lang);
8988 } else {
8989 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
8990 }
8991 }
8992 break;
8993 case 'q':
8994 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
8995 {
8996 struct timeval now;
8997 struct ast_tm tmnow;
8998 time_t beg_today;
8999
9000 gettimeofday(&now,NULL);
9001 ast_localtime(&now,&tmnow,timezone);
9002 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9003 /* In any case, it saves not having to do ast_mktime() */
9004 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9005 if (beg_today < time) {
9006 /* Today */
9007 } else if ((beg_today - 86400) < time) {
9008 /* Yesterday */
9009 res = wait_file(chan,ints, "digits/yesterday",lang);
9010 } else if (beg_today - 86400 * 6 < time) {
9011 /* Within the last week */
9012 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
9013 } else {
9014 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
9015 }
9016 }
9017 break;
9018 case 'R':
9019 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
9020 break;
9021 case 'S':
9022 /* Seconds */
9023 if (tm.tm_sec == 0) {
9024 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
9025 res = wait_file(chan,ints,nextmsg,lang);
9026 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
9027 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
9028 res = wait_file(chan,ints,nextmsg,lang);
9029 } else {
9030 int ten, one;
9031 ten = (tm.tm_sec / 10) * 10;
9032 one = (tm.tm_sec % 10);
9033 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
9034 res = wait_file(chan,ints,nextmsg,lang);
9035 if (!res) {
9036 /* Fifty, not fifty-zero */
9037 if (one != 0) {
9038 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
9039 res = wait_file(chan,ints,nextmsg,lang);
9040 }
9041 }
9042 }
9043 res = wait_file(chan,ints, "digits/byou",lang);
9044 break;
9045 case 'T':
9046 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
9047 break;
9048 case ' ':
9049 case ' ':
9050 /* Just ignore spaces and tabs */
9051 break;
9052 default:
9053 /* Unknown character */
9054 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9055 }
9056 /* Jump out on DTMF */
9057 if (res) {
9058 break;
9059 }
9060 }
9061 return res;
9062}
9063
9064/*! \brief Vietnamese syntax */
9065int 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)
9066{
9067 struct timeval when = { t, 0 };
9068 struct ast_tm tm;
9069 int res = 0, offset, sndoffset;
9070 char sndfile[256], nextmsg[256];
9071
9072 if (format == NULL)
9073 format = "A 'digits/day' eB 'digits/year' Y 'digits/at' k 'hours' M 'minutes' p";
9074
9075 ast_localtime(&when, &tm, tzone);
9076
9077 for (offset=0 ; format[offset] != '\0' ; offset++) {
9078 ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
9079 switch (format[offset]) {
9080 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
9081 case '\'':
9082 /* Literal name of a sound file */
9083 for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
9084 sndfile[sndoffset] = format[offset];
9085 }
9086 sndfile[sndoffset] = '\0';
9087 res = wait_file(chan, ints, sndfile, lang);
9088 break;
9089 case 'A':
9090 case 'a':
9091 /* Sunday - Saturday */
9092 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
9093 res = wait_file(chan, ints, nextmsg, lang);
9094 break;
9095 case 'B':
9096 case 'b':
9097 case 'h':
9098 /* January - December */
9099 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
9100 res = wait_file(chan, ints, nextmsg, lang);
9101 break;
9102 case 'm':
9103 /* Month enumerated */
9104 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
9105 break;
9106 case 'd':
9107 case 'e':
9108 /* 1 - 31 */
9109 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9110 break;
9111 case 'Y':
9112 /* Year */
9113 if (tm.tm_year > 99) {
9114 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9115 } else if (tm.tm_year < 1) {
9116 /* I'm not going to handle 1900 and prior */
9117 /* We'll just be silent on the year, instead of bombing out. */
9118 } else {
9119 res = wait_file(chan, ints, "digits/19", lang);
9120 if (!res) {
9121 if (tm.tm_year <= 9) {
9122 /* 1901 - 1909 */
9123 res = wait_file(chan, ints, "digits/odd", lang);
9124 }
9125
9126 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
9127 }
9128 }
9129 break;
9130 case 'I':
9131 case 'l':
9132 /* 12-Hour */
9133 if (tm.tm_hour == 0)
9134 ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
9135 else if (tm.tm_hour > 12)
9136 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
9137 else
9138 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
9139 res = wait_file(chan, ints, nextmsg, lang);
9140 break;
9141 case 'H':
9142 case 'k':
9143 /* 24-Hour */
9144 if (format[offset] == 'H') {
9145 /* e.g. oh-eight */
9146 if (tm.tm_hour < 10) {
9147 res = wait_file(chan, ints, "digits/0", lang);
9148 }
9149 } else {
9150 /* e.g. eight */
9151 if (tm.tm_hour == 0) {
9152 res = wait_file(chan, ints, "digits/0", lang);
9153 }
9154 }
9155 if (!res) {
9156 if (tm.tm_hour != 0) {
9157 int remaining = tm.tm_hour;
9158 if (tm.tm_hour > 20) {
9159 res = wait_file(chan, ints, "digits/20", lang);
9160 remaining -= 20;
9161 }
9162 if (!res) {
9163 snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
9164 res = wait_file(chan, ints, nextmsg, lang);
9165 }
9166 }
9167 }
9168 break;
9169 case 'M':
9170 case 'N':
9171 /* Minute */
9172 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
9173 break;
9174 case 'P':
9175 case 'p':
9176 /* AM/PM */
9177 if (tm.tm_hour > 11)
9178 ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
9179 else
9180 ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
9181 res = wait_file(chan, ints, nextmsg, lang);
9182 break;
9183 case 'Q':
9184 /* Shorthand for "Today", "Yesterday", or ABdY */
9185 /* XXX As emphasized elsewhere, this should the native way in your
9186 * language to say the date, with changes in what you say, depending
9187 * upon how recent the date is. XXX */
9188 {
9189 struct timeval now = ast_tvnow();
9190 struct ast_tm tmnow;
9191 time_t beg_today;
9192
9193 gettimeofday(&now, NULL);
9194 ast_localtime(&now, &tmnow, tzone);
9195 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9196 /* In any case, it saves not having to do ast_mktime() */
9197 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9198 if (beg_today < t) {
9199 /* Today */
9200 res = wait_file(chan, ints, "digits/today", lang);
9201 } else if (beg_today - 86400 < t) {
9202 /* Yesterday */
9203 res = wait_file(chan, ints, "digits/yesterday", lang);
9204 } else if (beg_today - 86400 * 6 < t) {
9205 /* Within the last week */
9206 res = ast_say_date_with_format_vi(chan, t, ints, lang, "A", tzone);
9207 } else if (beg_today - 2628000 < t) {
9208 /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9209 res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9210 } else if (beg_today - 15768000 < t) {
9211 /* Less than 6 months ago - "August seventh" */
9212 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9213 } else {
9214 /* More than 6 months ago - "April nineteenth two thousand three" */
9215 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9216 }
9217 }
9218 break;
9219 case 'q':
9220 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9221 /* XXX As emphasized elsewhere, this should the native way in your
9222 * language to say the date, with changes in what you say, depending
9223 * upon how recent the date is. XXX */
9224 {
9225 struct timeval now;
9226 struct ast_tm tmnow;
9227 time_t beg_today;
9228
9229 now = ast_tvnow();
9230 ast_localtime(&now, &tmnow, tzone);
9231 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9232 /* In any case, it saves not having to do ast_mktime() */
9233 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9234 if (beg_today < t) {
9235 /* Today */
9236 } else if ((beg_today - 86400) < t) {
9237 /* Yesterday */
9238 res = wait_file(chan, ints, "digits/yesterday", lang);
9239 } else if (beg_today - 86400 * 6 < t) {
9240 /* Within the last week */
9241 res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
9242 } else if (beg_today - 2628000 < t) {
9243 /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9244 res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9245 } else if (beg_today - 15768000 < t) {
9246 /* Less than 6 months ago - "August seventh" */
9247 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9248 } else {
9249 /* More than 6 months ago - "April nineteenth two thousand three" */
9250 res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9251 }
9252 }
9253 break;
9254 case 'R':
9255 res = ast_say_date_with_format_vi(chan, t, ints, lang, "HM", tzone);
9256 break;
9257 case 'S':
9258 /* Seconds */
9259 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
9260 break;
9261 case 'T':
9262 res = ast_say_date_with_format_vi(chan, t, ints, lang, "H 'hours' M 'minutes' S 'seconds'", tzone);
9263 break;
9264 case ' ':
9265 case ' ':
9266 /* Just ignore spaces and tabs */
9267 break;
9268 default:
9269 /* Unknown character */
9270 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9271 }
9272 /* Jump out on DTMF */
9273 if (res) {
9274 break;
9275 }
9276 }
9277 return res;
9278}
9279
9280/*! \brief Georgian support
9281
9282 Convert a number into a semi-localized string. Only for Georgian.
9283 res must be of at least 256 bytes, preallocated.
9284 The output corresponds to Georgian spoken numbers, so
9285 it may be either converted to real words by applying a direct conversion
9286 table, or played just by substituting the entities with played files.
9287
9288 Output may consist of the following tokens (separated by spaces):
9289 0, minus.
9290 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
9291 10-19.
9292 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
9293 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
9294 1000, 1000_. (atasi, atas).
9295 1000000, 1000000_. (milioni, milion).
9296 1000000000, 1000000000_. (miliardi, miliard).
9297
9298 To be able to play the sounds, each of the above tokens needs
9299 a corresponding sound file. (e.g. 200_.gsm).
9300*/
9301static char* ast_translate_number_ka(int num, char* res, int res_len)
9302{
9303 char buf[256];
9304 int digit = 0;
9305 int remaining = 0;
9306
9307
9308 if (num < 0) {
9309 strncat(res, "minus ", res_len - strlen(res) - 1);
9310 if ( num > INT_MIN ) {
9311 num = -num;
9312 } else {
9313 num = 0;
9314 }
9315 }
9316
9317
9318 /* directly read the numbers */
9319 if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
9320 snprintf(buf, sizeof(buf), "%d", num);
9321 strncat(res, buf, res_len - strlen(res) - 1);
9322 return res;
9323 }
9324
9325
9326 if (num < 40) { /* ocda... */
9327 strncat(res, "20_ ", res_len - strlen(res) - 1);
9328 return ast_translate_number_ka(num - 20, res, res_len);
9329 }
9330
9331 if (num < 60) { /* ormocda... */
9332 strncat(res, "40_ ", res_len - strlen(res) - 1);
9333 return ast_translate_number_ka(num - 40, res, res_len);
9334 }
9335
9336 if (num < 80) { /* samocda... */
9337 strncat(res, "60_ ", res_len - strlen(res) - 1);
9338 return ast_translate_number_ka(num - 60, res, res_len);
9339 }
9340
9341 if (num < 100) { /* otxmocda... */
9342 strncat(res, "80_ ", res_len - strlen(res) - 1);
9343 return ast_translate_number_ka(num - 80, res, res_len);
9344 }
9345
9346
9347 if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
9348 remaining = num % 100;
9349 digit = (num - remaining) / 100;
9350
9351 if (remaining == 0) {
9352 snprintf(buf, sizeof(buf), "%d", num);
9353 strncat(res, buf, res_len - strlen(res) - 1);
9354 return res;
9355 } else {
9356 snprintf(buf, sizeof(buf), "%d_ ", digit*100);
9357 strncat(res, buf, res_len - strlen(res) - 1);
9358 return ast_translate_number_ka(remaining, res, res_len);
9359 }
9360 }
9361
9362
9363 if (num == 1000) {
9364 strncat(res, "1000", res_len - strlen(res) - 1);
9365 return res;
9366 }
9367
9368
9369 if (num < 1000000) {
9370 remaining = num % 1000;
9371 digit = (num - remaining) / 1000;
9372
9373 if (remaining == 0) {
9374 ast_translate_number_ka(digit, res, res_len);
9375 strncat(res, " 1000", res_len - strlen(res) - 1);
9376 return res;
9377 }
9378
9379 if (digit == 1) {
9380 strncat(res, "1000_ ", res_len - strlen(res) - 1);
9381 return ast_translate_number_ka(remaining, res, res_len);
9382 }
9383
9384 ast_translate_number_ka(digit, res, res_len);
9385 strncat(res, " 1000_ ", res_len - strlen(res) - 1);
9386 return ast_translate_number_ka(remaining, res, res_len);
9387 }
9388
9389
9390 if (num == 1000000) {
9391 strncat(res, "1 1000000", res_len - strlen(res) - 1);
9392 return res;
9393 }
9394
9395
9396 if (num < 1000000000) {
9397 remaining = num % 1000000;
9398 digit = (num - remaining) / 1000000;
9399
9400 if (remaining == 0) {
9401 ast_translate_number_ka(digit, res, res_len);
9402 strncat(res, " 1000000", res_len - strlen(res) - 1);
9403 return res;
9404 }
9405
9406 ast_translate_number_ka(digit, res, res_len);
9407 strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
9408 return ast_translate_number_ka(remaining, res, res_len);
9409 }
9410
9411
9412 if (num == 1000000000) {
9413 strncat(res, "1 1000000000", res_len - strlen(res) - 1);
9414 return res;
9415 }
9416
9417
9418 if (num > 1000000000) {
9419 remaining = num % 1000000000;
9420 digit = (num - remaining) / 1000000000;
9421
9422 if (remaining == 0) {
9423 ast_translate_number_ka(digit, res, res_len);
9424 strncat(res, " 1000000000", res_len - strlen(res) - 1);
9425 return res;
9426 }
9427
9428 ast_translate_number_ka(digit, res, res_len);
9429 strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
9430 return ast_translate_number_ka(remaining, res, res_len);
9431 }
9432
9433 return res;
9434
9435}
9436
9437
9438
9439/*! \brief ast_say_number_full_ka: Georgian syntax */
9440static 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)
9441{
9442 int res = 0;
9443 char fn[512] = "";
9444 char* s = 0;
9445 const char* remaining = fn;
9446
9447 if (!num) {
9448 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
9449 }
9450
9451
9452 ast_translate_number_ka(num, fn, 512);
9453
9454
9455
9456 while (res == 0 && (s = strstr(remaining, " "))) {
9457 size_t len = s - remaining;
9458 char* new_string = ast_malloc(len + 1 + strlen("digits/"));
9459
9460 sprintf(new_string, "digits/");
9461 strncat(new_string, remaining, len); /* we can't sprintf() it, it's not null-terminated. */
9462/* new_string[len + strlen("digits/")] = '\0'; */
9463
9464 if (!ast_streamfile(chan, new_string, language)) {
9465 if ((audiofd > -1) && (ctrlfd > -1)) {
9466 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9467 } else {
9468 res = ast_waitstream(chan, ints);
9469 }
9470 }
9471 ast_stopstream(chan);
9472
9473 ast_free(new_string);
9474
9475 remaining = s + 1; /* position just after the found space char. */
9476 while (*remaining == ' ') { /* skip multiple spaces */
9477 remaining++;
9478 }
9479 }
9480
9481
9482 /* the last chunk. */
9483 if (res == 0 && *remaining) {
9484
9485 char* new_string = ast_malloc(strlen(remaining) + 1 + strlen("digits/"));
9486 sprintf(new_string, "digits/%s", remaining);
9487
9488 if (!ast_streamfile(chan, new_string, language)) {
9489 if ((audiofd > -1) && (ctrlfd > -1)) {
9490 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9491 } else {
9492 res = ast_waitstream(chan, ints);
9493 }
9494 }
9495 ast_stopstream(chan);
9496
9497 ast_free(new_string);
9498
9499 }
9500
9501
9502 return res;
9503
9504}
9505
9506
9507
9508/*! \brief Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
9509
9510Georgian support for date/time requires the following files (*.gsm):
9511
9512 - mon-1, mon-2, ... (ianvari, tebervali, ...)
9513 - day-1, day-2, ... (orshabati, samshabati, ...)
9514 - saati_da
9515 - tsuti
9516 - tslis
9517*/
9518static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9519{
9520 struct timeval when = { t, 0 };
9521 struct ast_tm tm;
9522 char fn[256];
9523 int res = 0;
9524 ast_localtime(&when, &tm, NULL);
9525
9526 if (!res) {
9527 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9528 }
9529
9530 if (!res) {
9531 snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
9532 res = ast_streamfile(chan, fn, lang);
9533 if (!res) {
9534 res = ast_waitstream(chan, ints);
9535 }
9536 }
9537
9538 if (!res) {
9539 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
9540/* if (!res)
9541 res = ast_waitstream(chan, ints);
9542*/
9543 }
9544
9545 if (!res) {
9546 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9547 res = ast_streamfile(chan, fn, lang);
9548 if (!res) {
9549 res = ast_waitstream(chan, ints);
9550 }
9551 }
9552 return res;
9553
9554}
9555
9556
9557
9558
9559
9560/*! \brief Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
9561static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9562{
9563 struct timeval when = { t, 0 };
9564 struct ast_tm tm;
9565 int res = 0;
9566
9567 ast_localtime(&when, &tm, NULL);
9568
9569 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
9570 if (!res) {
9571 res = ast_streamfile(chan, "digits/saati_da", lang);
9572 if (!res) {
9573 res = ast_waitstream(chan, ints);
9574 }
9575 }
9576
9577 if (tm.tm_min) {
9578 if (!res) {
9579 res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
9580
9581 if (!res) {
9582 res = ast_streamfile(chan, "digits/tsuti", lang);
9583 if (!res) {
9584 res = ast_waitstream(chan, ints);
9585 }
9586 }
9587 }
9588 }
9589 return res;
9590}
9591
9592
9593
9594/*! \brief Georgian syntax. Say date, then say time. */
9595static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9596{
9597 struct timeval when = { t, 0 };
9598 struct ast_tm tm;
9599 int res = 0;
9600
9601 ast_localtime(&when, &tm, NULL);
9602 res = ast_say_date(chan, t, ints, lang);
9603 if (!res) {
9604 ast_say_time(chan, t, ints, lang);
9605 }
9606 return res;
9607
9608}
9609
9610
9611
9612
9613/*! \brief Georgian syntax */
9614static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9615{
9616 int res=0;
9617 int daydiff;
9618 struct ast_tm tm;
9619 struct ast_tm now;
9620 struct timeval when = { t, 0 }, nowt = ast_tvnow();
9621 char fn[256];
9622
9623 ast_localtime(&when, &tm, NULL);
9624 ast_localtime(&nowt, &now, NULL);
9625 daydiff = now.tm_yday - tm.tm_yday;
9626 if ((daydiff < 0) || (daydiff > 6)) {
9627 /* Day of month and month */
9628 if (!res) {
9629 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9630 }
9631 if (!res) {
9632 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9633 res = ast_streamfile(chan, fn, lang);
9634 if (!res) {
9635 res = ast_waitstream(chan, ints);
9636 }
9637 }
9638
9639 } else if (daydiff) {
9640 /* Just what day of the week */
9641 if (!res) {
9642 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
9643 res = ast_streamfile(chan, fn, lang);
9644 if (!res) {
9645 res = ast_waitstream(chan, ints);
9646 }
9647 }
9648 } /* Otherwise, it was today */
9649 if (!res) {
9650 res = ast_say_time(chan, t, ints, lang);
9651 }
9652
9653 return res;
9654}
9655
9656/*! \brief
9657 * In English, we use the plural for everything but one. For example:
9658 * - 1 degree
9659 * - 2 degrees
9660 * - 5 degrees
9661 * The filename for the plural form is generated by appending "s". Note that
9662 * purpose is to generate a unique filename, not to implement irregular
9663 * declensions. Thus:
9664 * - 1 man
9665 * - 2 mans (the "mans" soundfile will of course say "men")
9666 */
9667static const char *counted_noun_ending_en(int num)
9668{
9669 if (num == 1 || num == -1) {
9670 return "";
9671 } else {
9672 return "s";
9673 }
9674}
9675
9676/*! \brief
9677 * Counting of objects in slavic languages such as Russian and Ukrainian the
9678 * rules are more complicated. There are two plural forms used in counting.
9679 * They are the genative singular which we represent with the suffix "x1" and
9680 * the genative plural which we represent with the suffix "x2". The base names
9681 * of the soundfiles remain in English. For example:
9682 * - 1 degree (soundfile says "gradus")
9683 * - 2 degreex1 (soundfile says "gradusa")
9684 * - 5 degreex2 (soundfile says "gradusov")
9685 */
9686static const char *counted_noun_ending_slavic(int num)
9687{
9688 if (num < 0) {
9689 num *= -1;
9690 }
9691 num %= 100; /* never pay attention to more than two digits */
9692 if (num >= 20) { /* for numbers 20 and above, pay attention to only last digit */
9693 num %= 10;
9694 }
9695 if (num == 1) { /* singular */
9696 return "";
9697 }
9698 if (num > 0 && num < 5) { /* 2--4 get genative singular */
9699 return "x1";
9700 } else { /* 5--19 get genative plural */
9701 return "x2";
9702 }
9703}
9704
9705int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
9706{
9707 char *temp;
9708 int temp_len;
9709 const char *ending;
9710 if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
9711 ending = counted_noun_ending_slavic(num);
9712 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
9713 ending = counted_noun_ending_slavic(num);
9714 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
9715 ending = counted_noun_ending_slavic(num);
9716 } else { /* English and default */
9717 ending = counted_noun_ending_en(num);
9718 }
9719 temp = ast_alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
9720 snprintf(temp, temp_len, "%s%s", noun, ending);
9721 return ast_play_and_wait(chan, temp);
9722}
9723
9724/*! \brief
9725 * In slavic languages such as Russian and Ukrainian the rules for declining
9726 * adjectives are simpler than those for nouns. When counting we use only
9727 * the singular (to which we give no suffix) and the genative plural (which
9728 * we represent by adding an "x"). Oh, an in the singular gender matters
9729 * so we append the supplied gender suffix ("m", "f", "n").
9730 */
9731static const char *counted_adjective_ending_ru(int num, const char gender[])
9732{
9733 if (num < 0) {
9734 num *= -1;
9735 }
9736 num %= 100; /* never pay attention to more than two digits */
9737 if (num >= 20) { /* at 20 and beyond only the last digit matters */
9738 num %= 10;
9739 }
9740 if (num == 1) {
9741 return gender ? gender : "";
9742 } else { /* all other numbers get the genative plural */
9743 return "x";
9744 }
9745}
9746
9747int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
9748{
9749 char *temp;
9750 int temp_len;
9751 const char *ending;
9752 if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
9753 ending = counted_adjective_ending_ru(num, gender);
9754 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
9755 ending = counted_adjective_ending_ru(num, gender);
9756 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
9757 ending = counted_adjective_ending_ru(num, gender);
9758 } else { /* English and default */
9759 ending = "";
9760 }
9761 temp = ast_alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
9762 snprintf(temp, temp_len, "%s%s", adjective, ending);
9763 return ast_play_and_wait(chan, temp);
9764}
9765
9766
9767
9768/*! \brief
9769 * remap the 'say' functions to use those in this file
9770 */
9771static void __attribute__((constructor)) __say_init(void)
9772{
9785}
char digit
const char * str
Definition: app_jack.c:147
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 int tmp()
Definition: bt_open.c:389
static char language[MAX_LANGUAGE]
Definition: chan_iax2.c:324
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)
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:1849
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
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:1616
char * strsep(char **str, const char *delims)
#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:555
static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:3970
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:5798
#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:3229
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:5607
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:8161
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:3111
static struct ast_str * get_number_str_en(int num, const char *lang)
Definition: say.c:444
static int exp10_int(int power)
Definition: say.c:924
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:1947
static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:7972
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:7790
static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:7206
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:6226
static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:7704
static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:7436
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:3760
static int get_lastdigits_ru(int num)
determine last digits for thousands/millions (ru)
Definition: say.c:2935
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:8067
static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:7290
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:9614
static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7723
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:347
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:2583
static char * pl_append(char *buffer, char *str)
Definition: say.c:2305
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:8275
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:9595
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:293
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:4333
#define IL_DATE_STR_FULL
static void __say_init(void)
remap the 'say' functions to use those in this file
Definition: say.c:9771
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:2876
struct ast_str * ast_get_ordinal_str(int num, const char *lang)
ast_get_ordinal_str: call language-specific functions
Definition: say.c:664
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:2102
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:8117
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:954
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:9065
static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:7496
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:3671
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:2962
static char * pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
Definition: say.c:2292
static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:8558
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:1166
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:8797
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:6429
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:9731
static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8350
static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:4224
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:3326
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:1827
static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:4130
static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
Definition: say.c:838
static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Danish syntax.
Definition: say.c:3999
static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:4048
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
Definition: say.c:9747
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:5275
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:63
static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8525
#define SAY_NUM_BUF_SIZE
Definition: say.c:1574
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:1379
static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:4159
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:4652
static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:7907
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:9440
static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:7357
struct ast_str * ast_get_number_str(int num, const char *lang)
ast_get_number_str: call language-specific functions
Definition: say.c:544
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:5063
#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:209
static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7567
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:1300
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:4854
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:1575
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:918
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:215
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:1747
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:2200
static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8430
static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:7310
static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:4285
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:1483
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:8029
static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7241
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:5994
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:2763
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:8625
static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:8478
static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:7649
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:5460
struct ast_str * ast_get_money_str(const char *str, const char *lang)
ast_get_money_str: call language-specific functions
Definition: say.c:427
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:3041
static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7663
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:7454
static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7333
static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:4098
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:8206
static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:7532
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:2683
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:6938
static struct ast_str * ast_get_money_en_dollars_str(const char *str, const char *lang)
Definition: say.c:353
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:1052
static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:8386
static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:7634
static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7377
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:911
static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:4251
static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
Definition: say.c:2312
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:7847
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:9686
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:9561
static char next_item(const char *format)
Definition: say.c:4645
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:299
static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:4188
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:438
int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
Definition: say.c:9705
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:7991
static char * ast_translate_number_ka(int num, char *res, int res_len)
Georgian support.
Definition: say.c:9301
static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:3935
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:852
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:2487
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:3346
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:2326
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:7406
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:174
static const char * counted_noun_ending_en(int num)
In English, we use the plural for everything but one. For example:
Definition: say.c:9667
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:6646
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:4374
static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:7804
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:3207
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:3509
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:9518
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:8235
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:8247
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:8283
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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
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
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * 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
Definition: say.c:2282
char * separator_dziesiatek
Definition: say.c:2283
char * cyfry2[10]
Definition: say.c:2285
char * cyfry[10]
Definition: say.c:2284
char * rzedy[3][3]
Definition: say.c:2289
char * nastki[10]
Definition: say.c:2288
char * setki[10]
Definition: say.c:2286
char * dziesiatki[10]
Definition: say.c:2287
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.