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