revert the dynamic_list insertion change... that was not the right thing to do
[asterisk-bristuff.git] / main / say.c
blobd51cda9864b7b2ad49ca093c27d4b61b21c8306e
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * George Konstantoulakis <gkon@inaccessnetworks.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
20 /*! \file
22 * \brief Say numbers and dates (maybe words one day too)
24 * \author Mark Spencer <markster@digium.com>
26 * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
30 #include "asterisk.h"
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <sys/types.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <netinet/in.h>
38 #include <time.h>
39 #include <ctype.h>
40 #include <math.h>
41 #include <stdio.h>
43 #ifdef SOLARIS
44 #include <iso/limits_iso.h>
45 #endif
47 #include "asterisk/file.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/options.h"
51 #include "asterisk/say.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/localtime.h"
54 #include "asterisk/utils.h"
56 /* Forward declaration */
57 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
60 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
62 const char *fn;
63 char fnbuf[256];
64 char ltr;
65 int num = 0;
66 int res = 0;
68 while (str[num]) {
69 fn = NULL;
70 switch (str[num]) {
71 case ('*'):
72 fn = "digits/star";
73 break;
74 case ('#'):
75 fn = "digits/pound";
76 break;
77 case ('!'):
78 fn = "letters/exclaimation-point";
79 break;
80 case ('@'):
81 fn = "letters/at";
82 break;
83 case ('$'):
84 fn = "letters/dollar";
85 break;
86 case ('-'):
87 fn = "letters/dash";
88 break;
89 case ('.'):
90 fn = "letters/dot";
91 break;
92 case ('='):
93 fn = "letters/equals";
94 break;
95 case ('+'):
96 fn = "letters/plus";
97 break;
98 case ('/'):
99 fn = "letters/slash";
100 break;
101 case (' '):
102 fn = "letters/space";
103 break;
104 case ('0'):
105 case ('1'):
106 case ('2'):
107 case ('3'):
108 case ('4'):
109 case ('5'):
110 case ('6'):
111 case ('7'):
112 case ('8'):
113 case ('9'):
114 strcpy(fnbuf, "digits/X");
115 fnbuf[7] = str[num];
116 fn = fnbuf;
117 break;
118 default:
119 ltr = str[num];
120 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
121 strcpy(fnbuf, "letters/X");
122 fnbuf[8] = ltr;
123 fn = fnbuf;
125 res = ast_streamfile(chan, fn, lang);
126 if (!res)
127 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
128 ast_stopstream(chan);
129 num++;
132 return res;
135 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
137 const char *fn;
138 char fnbuf[256];
139 char ltr;
140 int num = 0;
141 int res = 0;
143 while (str[num]) {
144 fn = NULL;
145 switch (str[num]) {
146 case ('*'):
147 fn = "digits/star";
148 break;
149 case ('#'):
150 fn = "digits/pound";
151 break;
152 case ('!'):
153 fn = "letters/exclaimation-point";
154 break;
155 case ('@'):
156 fn = "letters/at";
157 break;
158 case ('$'):
159 fn = "letters/dollar";
160 break;
161 case ('-'):
162 fn = "letters/dash";
163 break;
164 case ('.'):
165 fn = "letters/dot";
166 break;
167 case ('='):
168 fn = "letters/equals";
169 break;
170 case ('+'):
171 fn = "letters/plus";
172 break;
173 case ('/'):
174 fn = "letters/slash";
175 break;
176 case (' '):
177 fn = "letters/space";
178 break;
179 case ('0'):
180 case ('1'):
181 case ('2'):
182 case ('3'):
183 case ('4'):
184 case ('5'):
185 case ('6'):
186 case ('7'):
187 case ('8'):
188 strcpy(fnbuf, "digits/X");
189 fnbuf[7] = str[num];
190 fn = fnbuf;
191 break;
192 default: /* '9' falls here... */
193 ltr = str[num];
194 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
195 strcpy(fnbuf, "phonetic/X_p");
196 fnbuf[9] = ltr;
197 fn = fnbuf;
199 res = ast_streamfile(chan, fn, lang);
200 if (!res)
201 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
202 ast_stopstream(chan);
203 num++;
206 return res;
209 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
211 const char *fn;
212 char fnbuf[256];
213 int num = 0;
214 int res = 0;
216 while (str[num] && !res) {
217 fn = NULL;
218 switch (str[num]) {
219 case ('*'):
220 fn = "digits/star";
221 break;
222 case ('#'):
223 fn = "digits/pound";
224 break;
225 case ('-'):
226 fn = "digits/minus";
227 break;
228 case '0':
229 case '1':
230 case '2':
231 case '3':
232 case '4':
233 case '5':
234 case '6':
235 case '7':
236 case '8':
237 case '9':
238 strcpy(fnbuf, "digits/X");
239 fnbuf[7] = str[num];
240 fn = fnbuf;
241 break;
243 if (fn) {
244 res = ast_streamfile(chan, fn, lang);
245 if (!res)
246 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
247 ast_stopstream(chan);
249 num++;
252 return res;
255 /* Forward declarations */
256 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
257 \note Not really language codes.
258 For these language codes, Asterisk will change the syntax when
259 saying numbers (and in some cases dates and voicemail messages
260 as well)
261 \arg \b da - Danish
262 \arg \b de - German
263 \arg \b en - English (US)
264 \arg \b en_GB - English (British)
265 \arg \b es - Spanish, Mexican
266 \arg \b fr - French
267 \arg \b he - Hebrew
268 \arg \b it - Italian
269 \arg \b nl - Dutch
270 \arg \b no - Norwegian
271 \arg \b pl - Polish
272 \arg \b pt - Portuguese
273 \arg \b se - Swedish
274 \arg \b tw - Taiwanese / Chinese
275 \arg \b ru - Russian
277 \par Gender:
278 For Some languages the numbers differ for gender and plural.
279 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
280 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
281 use the option argument 'p' for plural enumerations like in German
283 Date/Time functions currently have less languages supported than saynumber().
285 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
287 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
289 \par Portuguese
290 Portuguese sound files needed for Time/Date functions:
291 pt-ah
292 pt-ao
293 pt-de
294 pt-e
295 pt-ora
296 pt-meianoite
297 pt-meiodia
298 pt-sss
300 \par Spanish
301 Spanish sound files needed for Time/Date functions:
302 es-de
303 es-el
305 \par Italian
306 Italian sound files needed for Time/Date functions:
307 ore-una
308 ore-mezzanotte
312 /* Forward declarations of language specific variants of ast_say_number_full */
313 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
314 static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
315 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);
316 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);
317 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
318 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);
319 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);
320 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);
321 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
322 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
323 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);
324 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);
325 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);
326 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);
327 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
328 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
329 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);
331 /* Forward declarations of language specific variants of ast_say_enumeration_full */
332 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
333 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);
334 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);
336 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
337 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
338 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
339 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
340 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
341 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
342 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
343 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
345 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
346 static int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
347 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
348 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
349 static int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
350 static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
351 static int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
352 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
353 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
354 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
355 static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
356 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
358 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
359 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
360 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
361 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
362 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
371 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
372 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
374 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
375 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
376 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
378 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
380 int res;
381 if ((res = ast_streamfile(chan, file, lang)))
382 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
383 if (!res)
384 res = ast_waitstream(chan, ints);
385 return res;
388 /*! \brief ast_say_number_full: call language-specific functions */
389 /* Called from AGI */
390 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
392 if (!strcasecmp(language,"en") ) { /* English syntax */
393 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
394 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
395 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
396 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
397 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
398 } else if (!strcasecmp(language, "de") ) { /* German syntax */
399 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
400 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
401 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
402 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
403 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
404 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
405 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
406 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
407 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
408 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
409 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
410 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
411 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
412 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
413 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
414 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
415 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
416 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
417 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
418 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
419 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
420 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
421 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
422 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
423 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
424 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
425 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
428 /* Default to english */
429 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
432 /*! \brief ast_say_number_full_en: English syntax */
433 /* This is the default syntax, if no other syntax defined in this file is used */
434 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
436 int res = 0;
437 int playh = 0;
438 char fn[256] = "";
439 if (!num)
440 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
442 while(!res && (num || playh)) {
443 if (num < 0) {
444 snprintf(fn, sizeof(fn), "digits/minus");
445 if ( num > INT_MIN ) {
446 num = -num;
447 } else {
448 num = 0;
450 } else if (playh) {
451 snprintf(fn, sizeof(fn), "digits/hundred");
452 playh = 0;
453 } else if (num < 20) {
454 snprintf(fn, sizeof(fn), "digits/%d", num);
455 num = 0;
456 } else if (num < 100) {
457 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
458 num -= ((num / 10) * 10);
459 } else {
460 if (num < 1000){
461 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
462 playh++;
463 num -= ((num / 100) * 100);
464 } else {
465 if (num < 1000000) { /* 1,000,000 */
466 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
467 if (res)
468 return res;
469 num = num % 1000;
470 snprintf(fn, sizeof(fn), "digits/thousand");
471 } else {
472 if (num < 1000000000) { /* 1,000,000,000 */
473 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
474 if (res)
475 return res;
476 num = num % 1000000;
477 snprintf(fn, sizeof(fn), "digits/million");
478 } else {
479 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
480 res = -1;
485 if (!res) {
486 if(!ast_streamfile(chan, fn, language)) {
487 if ((audiofd > -1) && (ctrlfd > -1))
488 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
489 else
490 res = ast_waitstream(chan, ints);
492 ast_stopstream(chan);
495 return res;
498 static int exp10_int(int power)
500 int x, res= 1;
501 for (x=0;x<power;x++)
502 res *= 10;
503 return res;
506 /*! \brief ast_say_number_full_cz: Czech syntax */
507 /* files needed:
508 * 1m,2m - gender male
509 * 1w,2w - gender female
510 * 3,4,...,20
511 * 30,40,...,90
513 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
515 * for each number 10^(3n + 3) exist 3 files represented as:
516 * 1 tousand = jeden tisic = 1_E3
517 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
518 * 5,6,... tousands = pet,sest,... tisic = 5_E3
520 * million = _E6
521 * miliard = _E9
522 * etc...
524 * tousand, milion are gender male, so 1 and 2 is 1m 2m
525 * miliard is gender female, so 1 and 2 is 1w 2w
527 static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
529 int res = 0;
530 int playh = 0;
531 char fn[256] = "";
533 int hundered = 0;
534 int left = 0;
535 int length = 0;
537 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
538 if (!options)
539 options = "w";
541 if (!num)
542 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
544 while(!res && (num || playh)) {
545 if (num < 0) {
546 snprintf(fn, sizeof(fn), "digits/minus");
547 if ( num > INT_MIN ) {
548 num = -num;
549 } else {
550 num = 0;
552 } else if (num < 3 ) {
553 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
554 playh = 0;
555 num = 0;
556 } else if (num < 20) {
557 snprintf(fn, sizeof(fn), "digits/%d",num);
558 playh = 0;
559 num = 0;
560 } else if (num < 100) {
561 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
562 num -= ((num / 10) * 10);
563 } else if (num < 1000) {
564 hundered = num / 100;
565 if ( hundered == 1 ) {
566 snprintf(fn, sizeof(fn), "digits/1sto");
567 } else if ( hundered == 2 ) {
568 snprintf(fn, sizeof(fn), "digits/2ste");
569 } else {
570 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
571 if (res)
572 return res;
573 if (hundered == 3 || hundered == 4) {
574 snprintf(fn, sizeof(fn), "digits/sta");
575 } else if ( hundered > 4 ) {
576 snprintf(fn, sizeof(fn), "digits/set");
579 num -= (hundered * 100);
580 } else { /* num > 1000 */
581 length = (int)log10(num)+1;
582 while ( (length % 3 ) != 1 ) {
583 length--;
585 left = num / (exp10_int(length-1));
586 if ( left == 2 ) {
587 switch (length-1) {
588 case 9: options = "w"; /* 1,000,000,000 gender female */
589 break;
590 default : options = "m"; /* others are male */
593 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
594 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
595 if (res)
596 return res;
598 if ( left >= 5 ) { /* >= 5 have the same declesion */
599 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
600 } else if ( left >= 2 && left <= 4 ) {
601 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
602 } else { /* left == 1 */
603 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
605 num -= left * (exp10_int(length-1));
607 if (!res) {
608 if(!ast_streamfile(chan, fn, language)) {
609 if ((audiofd > -1) && (ctrlfd > -1)) {
610 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
611 } else {
612 res = ast_waitstream(chan, ints);
615 ast_stopstream(chan);
618 return res;
621 /*! \brief ast_say_number_full_da: Danish syntax */
622 /* New files:
623 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
625 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)
627 int res = 0;
628 int playh = 0;
629 int playa = 0;
630 int cn = 1; /* +1 = commune; -1 = neuter */
631 char fn[256] = "";
632 if (!num)
633 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
635 if (options && !strncasecmp(options, "n",1)) cn = -1;
637 while(!res && (num || playh || playa )) {
638 /* The grammar for Danish numbers is the same as for English except
639 * for the following:
640 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
641 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
642 * "one-and twenty" and 68 is "eight-and sixty".
643 * - "million" is different in singular and plural form
644 * - numbers > 1000 with zero as the third digit from last have an
645 * "and" before the last two digits, i.e. 2034 is "two thousand and
646 * four-and thirty" and 1000012 is "one million and twelve".
648 if (num < 0) {
649 snprintf(fn, sizeof(fn), "digits/minus");
650 if ( num > INT_MIN ) {
651 num = -num;
652 } else {
653 num = 0;
655 } else if (playh) {
656 snprintf(fn, sizeof(fn), "digits/hundred");
657 playh = 0;
658 } else if (playa) {
659 snprintf(fn, sizeof(fn), "digits/and");
660 playa = 0;
661 } else if (num == 1 && cn == -1) {
662 snprintf(fn, sizeof(fn), "digits/1N");
663 num = 0;
664 } else if (num < 20) {
665 snprintf(fn, sizeof(fn), "digits/%d", num);
666 num = 0;
667 } else if (num < 100) {
668 int ones = num % 10;
669 if (ones) {
670 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
671 num -= ones;
672 } else {
673 snprintf(fn, sizeof(fn), "digits/%d", num);
674 num = 0;
676 } else {
677 if (num < 1000) {
678 int hundreds = num / 100;
679 if (hundreds == 1)
680 snprintf(fn, sizeof(fn), "digits/1N");
681 else
682 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
684 playh++;
685 num -= 100 * hundreds;
686 if (num)
687 playa++;
689 } else {
690 if (num < 1000000) {
691 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
692 if (res)
693 return res;
694 num = num % 1000;
695 snprintf(fn, sizeof(fn), "digits/thousand");
696 } else {
697 if (num < 1000000000) {
698 int millions = num / 1000000;
699 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
700 if (res)
701 return res;
702 if (millions == 1)
703 snprintf(fn, sizeof(fn), "digits/million");
704 else
705 snprintf(fn, sizeof(fn), "digits/millions");
706 num = num % 1000000;
707 } else {
708 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
709 res = -1;
712 if (num && num < 100)
713 playa++;
716 if (!res) {
717 if(!ast_streamfile(chan, fn, language)) {
718 if ((audiofd > -1) && (ctrlfd > -1))
719 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
720 else
721 res = ast_waitstream(chan, ints);
723 ast_stopstream(chan);
726 return res;
729 /*! \brief ast_say_number_full_de: German syntax */
730 /* New files:
731 In addition to English, the following sounds are required:
732 "millions"
733 "1-and" through "9-and"
734 "1F" (eine)
735 "1N" (ein)
736 NB "1" is recorded as 'eins'
738 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)
740 int res = 0, t = 0;
741 int mf = 1; /* +1 = male and neuter; -1 = female */
742 char fn[256] = "";
743 char fna[256] = "";
744 if (!num)
745 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
747 if (options && (!strncasecmp(options, "f",1)))
748 mf = -1;
750 while(!res && num) {
751 /* The grammar for German numbers is the same as for English except
752 * for the following:
753 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
754 * "one-and twenty" and 68 is "eight-and sixty".
755 * - "one" varies according to gender
756 * - 100 is 'hundert', however all other instances are 'ein hundert'
757 * - 1000 is 'tausend', however all other instances are 'ein tausend'
758 * - 1000000 is always 'eine million'
759 * - "million" is different in singular and plural form
761 if (num < 0) {
762 snprintf(fn, sizeof(fn), "digits/minus");
763 if ( num > INT_MIN ) {
764 num = -num;
765 } else {
766 num = 0;
768 } else if (num < 100 && t) {
769 snprintf(fn, sizeof(fn), "digits/and");
770 t = 0;
771 } else if (num == 1 && mf == -1) {
772 snprintf(fn, sizeof(fn), "digits/%dF", num);
773 num = 0;
774 } else if (num < 20) {
775 snprintf(fn, sizeof(fn), "digits/%d", num);
776 num = 0;
777 } else if (num < 100) {
778 int ones = num % 10;
779 if (ones) {
780 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
781 num -= ones;
782 } else {
783 snprintf(fn, sizeof(fn), "digits/%d", num);
784 num = 0;
786 } else if (num == 100 && t == 0) {
787 snprintf(fn, sizeof(fn), "digits/hundred");
788 num = 0;
789 } else if (num < 1000) {
790 int hundreds = num / 100;
791 num = num % 100;
792 if (hundreds == 1) {
793 snprintf(fn, sizeof(fn), "digits/1N");
794 } else {
795 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
797 snprintf(fna, sizeof(fna), "digits/hundred");
798 t = 1;
799 } else if (num == 1000 && t == 0) {
800 snprintf(fn, sizeof(fn), "digits/thousand");
801 num = 0;
802 } else if (num < 1000000) {
803 int thousands = num / 1000;
804 num = num % 1000;
805 t = 1;
806 if (thousands == 1) {
807 snprintf(fn, sizeof(fn), "digits/1N");
808 snprintf(fna, sizeof(fna), "digits/thousand");
809 } else {
810 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
811 if (res)
812 return res;
813 snprintf(fn, sizeof(fn), "digits/thousand");
815 } else if (num < 1000000000) {
816 int millions = num / 1000000;
817 num = num % 1000000;
818 t = 1;
819 if (millions == 1) {
820 snprintf(fn, sizeof(fn), "digits/1F");
821 snprintf(fna, sizeof(fna), "digits/million");
822 } else {
823 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
824 if (res)
825 return res;
826 snprintf(fn, sizeof(fn), "digits/millions");
828 } else if (num <= INT_MAX) {
829 int billions = num / 1000000000;
830 num = num % 1000000000;
831 t = 1;
832 if (billions == 1) {
833 snprintf(fn, sizeof(fn), "digits/1F");
834 snprintf(fna, sizeof(fna), "digits/milliard");
835 } else {
836 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
837 if (res) {
838 return res;
840 snprintf(fn, sizeof(fn), "digits/milliards");
842 } else {
843 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
844 res = -1;
846 if (!res) {
847 if(!ast_streamfile(chan, fn, language)) {
848 if ((audiofd > -1) && (ctrlfd > -1))
849 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
850 else
851 res = ast_waitstream(chan, ints);
853 ast_stopstream(chan);
854 if (!res) {
855 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
856 if ((audiofd > -1) && (ctrlfd > -1))
857 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
858 else
859 res = ast_waitstream(chan, ints);
861 ast_stopstream(chan);
862 strcpy(fna, "");
866 return res;
869 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
870 /* New files:
871 In addition to American English, the following sounds are required: "and"
873 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
875 int res = 0;
876 int playh = 0;
877 int playa = 0;
878 char fn[256] = "";
879 if (!num)
880 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
882 while(!res && (num || playh || playa )) {
883 if (num < 0) {
884 snprintf(fn, sizeof(fn), "digits/minus");
885 if ( num > INT_MIN ) {
886 num = -num;
887 } else {
888 num = 0;
890 } else if (playh) {
891 snprintf(fn, sizeof(fn), "digits/hundred");
892 playh = 0;
893 } else if (playa) {
894 snprintf(fn, sizeof(fn), "digits/and");
895 playa = 0;
896 } else if (num < 20) {
897 snprintf(fn, sizeof(fn), "digits/%d", num);
898 num = 0;
899 } else if (num < 100) {
900 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
901 num -= ((num / 10) * 10);
902 } else if (num < 1000) {
903 int hundreds = num / 100;
904 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
906 playh++;
907 num -= 100 * hundreds;
908 if (num)
909 playa++;
910 } else if (num < 1000000) {
911 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
912 if (res)
913 return res;
914 snprintf(fn, sizeof(fn), "digits/thousand");
915 num = num % 1000;
916 if (num && num < 100)
917 playa++;
918 } else if (num < 1000000000) {
919 int millions = num / 1000000;
920 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
921 if (res)
922 return res;
923 snprintf(fn, sizeof(fn), "digits/million");
924 num = num % 1000000;
925 if (num && num < 100)
926 playa++;
927 } else {
928 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
929 res = -1;
932 if (!res) {
933 if(!ast_streamfile(chan, fn, language)) {
934 if ((audiofd > -1) && (ctrlfd > -1))
935 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
936 else
937 res = ast_waitstream(chan, ints);
939 ast_stopstream(chan);
942 return res;
945 /*! \brief ast_say_number_full_es: Spanish syntax */
946 /* New files:
947 Requires a few new audios:
948 1F.gsm: feminine 'una'
949 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
951 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)
953 int res = 0;
954 int playa = 0;
955 int mf = 0; /* +1 = male; -1 = female */
956 char fn[256] = "";
957 if (!num)
958 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
960 if (options) {
961 if (!strncasecmp(options, "f",1))
962 mf = -1;
963 else if (!strncasecmp(options, "m", 1))
964 mf = 1;
967 while (!res && num) {
968 if (num < 0) {
969 snprintf(fn, sizeof(fn), "digits/minus");
970 if ( num > INT_MIN ) {
971 num = -num;
972 } else {
973 num = 0;
975 } else if (playa) {
976 snprintf(fn, sizeof(fn), "digits/and");
977 playa = 0;
978 } else if (num == 1) {
979 if (mf < 0)
980 snprintf(fn, sizeof(fn), "digits/%dF", num);
981 else if (mf > 0)
982 snprintf(fn, sizeof(fn), "digits/%dM", num);
983 else
984 snprintf(fn, sizeof(fn), "digits/%d", num);
985 num = 0;
986 } else if (num < 31) {
987 snprintf(fn, sizeof(fn), "digits/%d", num);
988 num = 0;
989 } else if (num < 100) {
990 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
991 num -= ((num/10)*10);
992 if (num)
993 playa++;
994 } else if (num == 100) {
995 snprintf(fn, sizeof(fn), "digits/100");
996 num = 0;
997 } else if (num < 200) {
998 snprintf(fn, sizeof(fn), "digits/100-and");
999 num -= 100;
1000 } else {
1001 if (num < 1000) {
1002 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1003 num -= ((num/100)*100);
1004 } else if (num < 2000) {
1005 num = num % 1000;
1006 snprintf(fn, sizeof(fn), "digits/thousand");
1007 } else {
1008 if (num < 1000000) {
1009 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1010 if (res)
1011 return res;
1012 num = num % 1000;
1013 snprintf(fn, sizeof(fn), "digits/thousand");
1014 } else {
1015 if (num < 2147483640) {
1016 if ((num/1000000) == 1) {
1017 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1018 if (res)
1019 return res;
1020 snprintf(fn, sizeof(fn), "digits/million");
1021 } else {
1022 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1023 if (res)
1024 return res;
1025 snprintf(fn, sizeof(fn), "digits/millions");
1027 num = num % 1000000;
1028 } else {
1029 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1030 res = -1;
1036 if (!res) {
1037 if(!ast_streamfile(chan, fn, language)) {
1038 if ((audiofd > -1) && (ctrlfd > -1))
1039 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1040 else
1041 res = ast_waitstream(chan, ints);
1043 ast_stopstream(chan);
1048 return res;
1051 /*! \brief ast_say_number_full_fr: French syntax */
1052 /* Extra sounds needed:
1053 1F: feminin 'une'
1054 et: 'and' */
1055 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)
1057 int res = 0;
1058 int playh = 0;
1059 int playa = 0;
1060 int mf = 1; /* +1 = male; -1 = female */
1061 char fn[256] = "";
1062 if (!num)
1063 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1065 if (options && !strncasecmp(options, "f",1))
1066 mf = -1;
1068 while(!res && (num || playh || playa)) {
1069 if (num < 0) {
1070 snprintf(fn, sizeof(fn), "digits/minus");
1071 if ( num > INT_MIN ) {
1072 num = -num;
1073 } else {
1074 num = 0;
1076 } else if (playh) {
1077 snprintf(fn, sizeof(fn), "digits/hundred");
1078 playh = 0;
1079 } else if (playa) {
1080 snprintf(fn, sizeof(fn), "digits/et");
1081 playa = 0;
1082 } else if (num == 1) {
1083 if (mf < 0)
1084 snprintf(fn, sizeof(fn), "digits/%dF", num);
1085 else
1086 snprintf(fn, sizeof(fn), "digits/%d", num);
1087 num = 0;
1088 } else if (num < 21) {
1089 snprintf(fn, sizeof(fn), "digits/%d", num);
1090 num = 0;
1091 } else if (num < 70) {
1092 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1093 if ((num % 10) == 1) playa++;
1094 num = num % 10;
1095 } else if (num < 80) {
1096 snprintf(fn, sizeof(fn), "digits/60");
1097 if ((num % 10) == 1) playa++;
1098 num = num - 60;
1099 } else if (num < 100) {
1100 snprintf(fn, sizeof(fn), "digits/80");
1101 num = num - 80;
1102 } else if (num < 200) {
1103 snprintf(fn, sizeof(fn), "digits/hundred");
1104 num = num - 100;
1105 } else if (num < 1000) {
1106 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1107 playh++;
1108 num = num % 100;
1109 } else if (num < 2000) {
1110 snprintf(fn, sizeof(fn), "digits/thousand");
1111 num = num - 1000;
1112 } else if (num < 1000000) {
1113 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1114 if (res)
1115 return res;
1116 snprintf(fn, sizeof(fn), "digits/thousand");
1117 num = num % 1000;
1118 } else if (num < 1000000000) {
1119 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1120 if (res)
1121 return res;
1122 snprintf(fn, sizeof(fn), "digits/million");
1123 num = num % 1000000;
1124 } else {
1125 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1126 res = -1;
1128 if (!res) {
1129 if(!ast_streamfile(chan, fn, language)) {
1130 if ((audiofd > -1) && (ctrlfd > -1))
1131 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1132 else
1133 res = ast_waitstream(chan, ints);
1135 ast_stopstream(chan);
1138 return res;
1143 /*! \brief ast_say_number_full_he: Hebrew syntax */
1144 /* Extra sounds needed:
1145 1F: feminin 'one'
1146 ve: 'and'
1147 1hundred: 1 hundred
1148 2hundred: 2 hundreds
1149 2thousands: 2 thousand
1150 thousands: plural of 'thousand'
1151 3sF 'Smichut forms (female)
1158 3s 'Smichut' forms (male)
1175 TODO: 've' should sometimed be 'hu':
1176 * before 'shtaym' (2, F)
1177 * before 'shnaym' (2, M)
1178 * before 'shlosha' (3, M)
1179 * before 'shmone' (8, M)
1180 * before 'shlosim' (30)
1181 * before 'shmonim' (80)
1183 What about:
1184 'sheva' (7, F)?
1185 'tesha' (9, F)?
1187 #define SAY_NUM_BUF_SIZE 256
1188 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1189 const char *ints, const char *language, const char *options,
1190 int audiofd, int ctrlfd)
1192 int res = 0;
1193 int state = 0; /* no need to save anything */
1194 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1195 char fn[SAY_NUM_BUF_SIZE] = "";
1196 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1197 "num: %d, options=\"%s\"\n",
1198 num, options
1200 if (!num)
1201 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1203 if (options && !strncasecmp(options, "f",1))
1204 mf = -1;
1206 /* Do we have work to do? */
1207 while(!res && (num || (state>0) )) {
1208 /* first type of work: play a second sound. In this loop
1209 * we can only play one sound file at a time. Thus playing
1210 * a second one requires repeating the loop just for the
1211 * second file. The variable 'state' remembers where we were.
1212 * state==0 is the normal mode and it means that we continue
1213 * to check if the number num has yet anything left.
1215 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1216 "state=%d, options=\"%s\", mf=%d\n",
1217 num, state, options, mf
1219 if (state==1) {
1220 snprintf(fn, sizeof(fn), "digits/hundred");
1221 state = 0;
1222 } else if (state==2) {
1223 snprintf(fn, sizeof(fn), "digits/ve");
1224 state = 0;
1225 } else if (state==3) {
1226 snprintf(fn, sizeof(fn), "digits/thousands");
1227 state=0;
1228 } else if (num <21) {
1229 if (mf < 0)
1230 snprintf(fn, sizeof(fn), "digits/%dF", num);
1231 else
1232 snprintf(fn, sizeof(fn), "digits/%d", num);
1233 num = 0;
1234 } else if (num < 100) {
1235 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1236 num = num % 10;
1237 if (num>0) state=2;
1238 } else if (num < 200) {
1239 snprintf(fn, sizeof(fn), "digits/1hundred");
1240 num = num - 100;
1241 state=2;
1242 } else if (num < 300) {
1243 snprintf(fn, sizeof(fn), "digits/2hundred");
1244 num = num - 200;
1245 state=2;
1246 } else if (num < 1000) {
1247 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1248 state=1;
1249 num = num % 100;
1250 } else if (num < 2000) {
1251 snprintf(fn, sizeof(fn), "digits/thousand");
1252 num = num - 1000;
1253 } else if (num < 3000) {
1254 snprintf(fn, sizeof(fn), "digits/2thousand");
1255 num = num - 2000;
1256 if (num>0) state=2;
1257 } else if (num < 20000) {
1258 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1259 num = num % 1000;
1260 state=3;
1261 } else if (num < 1000000) {
1262 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1263 if (res)
1264 return res;
1265 snprintf(fn, sizeof(fn), "digits/thousand");
1266 num = num % 1000;
1267 } else if (num < 1000000000) {
1268 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1269 if (res)
1270 return res;
1271 snprintf(fn, sizeof(fn), "digits/million");
1272 num = num % 1000000;
1273 } else {
1274 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1275 res = -1;
1277 if (!res) {
1278 if(!ast_streamfile(chan, fn, language)) {
1279 if ((audiofd > -1) && (ctrlfd > -1))
1280 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1281 else
1282 res = ast_waitstream(chan, ints);
1284 ast_stopstream(chan);
1287 return res;
1290 /*! \brief ast_say_number_full_it: Italian */
1291 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1293 int res = 0;
1294 int playh = 0;
1295 int tempnum = 0;
1296 char fn[256] = "";
1298 if (!num)
1299 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1302 Italian support
1304 Like english, numbers up to 20 are a single 'word', and others
1305 compound, but with exceptions.
1306 For example 21 is not twenty-one, but there is a single word in 'it'.
1307 Idem for 28 (ie when a the 2nd part of a compund number
1308 starts with a vowel)
1310 There are exceptions also for hundred, thousand and million.
1311 In english 100 = one hundred, 200 is two hundred.
1312 In italian 100 = cento , like to say hundred (without one),
1313 200 and more are like english.
1315 Same applies for thousand:
1316 1000 is one thousand in en, 2000 is two thousand.
1317 In it we have 1000 = mille , 2000 = 2 mila
1319 For million(s) we use the plural, if more than one
1320 Also, one million is abbreviated in it, like on-million,
1321 or 'un milione', not 'uno milione'.
1322 So the right file is provided.
1325 while(!res && (num || playh)) {
1326 if (num < 0) {
1327 snprintf(fn, sizeof(fn), "digits/minus");
1328 if ( num > INT_MIN ) {
1329 num = -num;
1330 } else {
1331 num = 0;
1333 } else if (playh) {
1334 snprintf(fn, sizeof(fn), "digits/hundred");
1335 playh = 0;
1336 } else if (num < 20) {
1337 snprintf(fn, sizeof(fn), "digits/%d", num);
1338 num = 0;
1339 } else if (num == 21) {
1340 snprintf(fn, sizeof(fn), "digits/%d", num);
1341 num = 0;
1342 } else if (num == 28) {
1343 snprintf(fn, sizeof(fn), "digits/%d", num);
1344 num = 0;
1345 } else if (num == 31) {
1346 snprintf(fn, sizeof(fn), "digits/%d", num);
1347 num = 0;
1348 } else if (num == 38) {
1349 snprintf(fn, sizeof(fn), "digits/%d", num);
1350 num = 0;
1351 } else if (num == 41) {
1352 snprintf(fn, sizeof(fn), "digits/%d", num);
1353 num = 0;
1354 } else if (num == 48) {
1355 snprintf(fn, sizeof(fn), "digits/%d", num);
1356 num = 0;
1357 } else if (num == 51) {
1358 snprintf(fn, sizeof(fn), "digits/%d", num);
1359 num = 0;
1360 } else if (num == 58) {
1361 snprintf(fn, sizeof(fn), "digits/%d", num);
1362 num = 0;
1363 } else if (num == 61) {
1364 snprintf(fn, sizeof(fn), "digits/%d", num);
1365 num = 0;
1366 } else if (num == 68) {
1367 snprintf(fn, sizeof(fn), "digits/%d", num);
1368 num = 0;
1369 } else if (num == 71) {
1370 snprintf(fn, sizeof(fn), "digits/%d", num);
1371 num = 0;
1372 } else if (num == 78) {
1373 snprintf(fn, sizeof(fn), "digits/%d", num);
1374 num = 0;
1375 } else if (num == 81) {
1376 snprintf(fn, sizeof(fn), "digits/%d", num);
1377 num = 0;
1378 } else if (num == 88) {
1379 snprintf(fn, sizeof(fn), "digits/%d", num);
1380 num = 0;
1381 } else if (num == 91) {
1382 snprintf(fn, sizeof(fn), "digits/%d", num);
1383 num = 0;
1384 } else if (num == 98) {
1385 snprintf(fn, sizeof(fn), "digits/%d", num);
1386 num = 0;
1387 } else if (num < 100) {
1388 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1389 num -= ((num / 10) * 10);
1390 } else {
1391 if (num < 1000) {
1392 if ((num / 100) > 1) {
1393 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1394 playh++;
1395 } else {
1396 snprintf(fn, sizeof(fn), "digits/hundred");
1398 num -= ((num / 100) * 100);
1399 } else {
1400 if (num < 1000000) { /* 1,000,000 */
1401 if ((num/1000) > 1)
1402 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1403 if (res)
1404 return res;
1405 tempnum = num;
1406 num = num % 1000;
1407 if ((tempnum / 1000) < 2)
1408 snprintf(fn, sizeof(fn), "digits/thousand");
1409 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1410 snprintf(fn, sizeof(fn), "digits/thousands");
1411 } else {
1412 if (num < 1000000000) { /* 1,000,000,000 */
1413 if ((num / 1000000) > 1)
1414 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1415 if (res)
1416 return res;
1417 tempnum = num;
1418 num = num % 1000000;
1419 if ((tempnum / 1000000) < 2)
1420 snprintf(fn, sizeof(fn), "digits/million");
1421 else
1422 snprintf(fn, sizeof(fn), "digits/millions");
1423 } else {
1424 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1425 res = -1;
1430 if (!res) {
1431 if(!ast_streamfile(chan, fn, language)) {
1432 if ((audiofd > -1) && (ctrlfd > -1))
1433 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1434 else
1435 res = ast_waitstream(chan, ints);
1437 ast_stopstream(chan);
1440 return res;
1443 /*! \brief ast_say_number_full_nl: dutch syntax */
1444 /* New files: digits/nl-en
1446 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1448 int res = 0;
1449 int playh = 0;
1450 int units = 0;
1451 char fn[256] = "";
1452 if (!num)
1453 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1454 while (!res && (num || playh )) {
1455 if (num < 0) {
1456 snprintf(fn, sizeof(fn), "digits/minus");
1457 if ( num > INT_MIN ) {
1458 num = -num;
1459 } else {
1460 num = 0;
1462 } else if (playh) {
1463 snprintf(fn, sizeof(fn), "digits/hundred");
1464 playh = 0;
1465 } else if (num < 20) {
1466 snprintf(fn, sizeof(fn), "digits/%d", num);
1467 num = 0;
1468 } else if (num < 100) {
1469 units = num % 10;
1470 if (units > 0) {
1471 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1472 if (res)
1473 return res;
1474 num = num - units;
1475 snprintf(fn, sizeof(fn), "digits/nl-en");
1476 } else {
1477 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1478 num = 0;
1480 } else {
1481 if (num < 1000) {
1482 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1483 playh++;
1484 num -= ((num / 100) * 100);
1485 } else {
1486 if (num < 1000000) { /* 1,000,000 */
1487 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1488 if (res)
1489 return res;
1490 num = num % 1000;
1491 snprintf(fn, sizeof(fn), "digits/thousand");
1492 } else {
1493 if (num < 1000000000) { /* 1,000,000,000 */
1494 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1495 if (res)
1496 return res;
1497 num = num % 1000000;
1498 snprintf(fn, sizeof(fn), "digits/million");
1499 } else {
1500 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1501 res = -1;
1507 if (!res) {
1508 if(!ast_streamfile(chan, fn, language)) {
1509 if ((audiofd > -1) && (ctrlfd > -1))
1510 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1511 else
1512 res = ast_waitstream(chan, ints);
1514 ast_stopstream(chan);
1517 return res;
1520 /*! \brief ast_say_number_full_no: Norwegian syntax */
1521 /* New files:
1522 In addition to American English, the following sounds are required: "and", "1N"
1524 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)
1526 int res = 0;
1527 int playh = 0;
1528 int playa = 0;
1529 int cn = 1; /* +1 = commune; -1 = neuter */
1530 char fn[256] = "";
1532 if (!num)
1533 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1535 if (options && !strncasecmp(options, "n",1)) cn = -1;
1537 while(!res && (num || playh || playa )) {
1538 /* The grammar for Norwegian numbers is the same as for English except
1539 * for the following:
1540 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1541 * "and" before the last two digits, i.e. 2034 is "two thousand and
1542 * thirty-four" and 1000012 is "one million and twelve".
1544 if (num < 0) {
1545 snprintf(fn, sizeof(fn), "digits/minus");
1546 if ( num > INT_MIN ) {
1547 num = -num;
1548 } else {
1549 num = 0;
1551 } else if (playh) {
1552 snprintf(fn, sizeof(fn), "digits/hundred");
1553 playh = 0;
1554 } else if (playa) {
1555 snprintf(fn, sizeof(fn), "digits/and");
1556 playa = 0;
1557 } else if (num == 1 && cn == -1) {
1558 snprintf(fn, sizeof(fn), "digits/1N");
1559 num = 0;
1560 } else if (num < 20) {
1561 snprintf(fn, sizeof(fn), "digits/%d", num);
1562 num = 0;
1563 } else if (num < 100) {
1564 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1565 num -= ((num / 10) * 10);
1566 } else if (num < 1000) {
1567 int hundreds = num / 100;
1568 if (hundreds == 1)
1569 snprintf(fn, sizeof(fn), "digits/1N");
1570 else
1571 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1573 playh++;
1574 num -= 100 * hundreds;
1575 if (num)
1576 playa++;
1577 } else if (num < 1000000) {
1578 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1579 if (res)
1580 return res;
1581 snprintf(fn, sizeof(fn), "digits/thousand");
1582 num = num % 1000;
1583 if (num && num < 100)
1584 playa++;
1585 } else if (num < 1000000000) {
1586 int millions = num / 1000000;
1587 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1588 if (res)
1589 return res;
1590 snprintf(fn, sizeof(fn), "digits/million");
1591 num = num % 1000000;
1592 if (num && num < 100)
1593 playa++;
1594 } else {
1595 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1596 res = -1;
1599 if (!res) {
1600 if(!ast_streamfile(chan, fn, language)) {
1601 if ((audiofd > -1) && (ctrlfd > -1))
1602 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1603 else
1604 res = ast_waitstream(chan, ints);
1606 ast_stopstream(chan);
1609 return res;
1612 typedef struct {
1613 char *separator_dziesiatek;
1614 char *cyfry[10];
1615 char *cyfry2[10];
1616 char *setki[10];
1617 char *dziesiatki[10];
1618 char *nastki[10];
1619 char *rzedy[3][3];
1620 } odmiana;
1622 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1624 if (rzad==0)
1625 return "";
1627 if (i==1)
1628 return odm->rzedy[rzad - 1][0];
1629 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1630 return odm->rzedy[rzad - 1][1];
1631 else
1632 return odm->rzedy[rzad - 1][2];
1635 static char* pl_append(char* buffer, char* str)
1637 strcpy(buffer, str);
1638 buffer += strlen(str);
1639 return buffer;
1642 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1644 char file_name[255] = "digits/";
1645 strcat(file_name, fn);
1646 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1647 if (!ast_streamfile(chan, file_name, language)) {
1648 if ((audiofd > -1) && (ctrlfd > -1))
1649 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1650 else
1651 ast_waitstream(chan, ints);
1653 ast_stopstream(chan);
1656 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1658 /* Initialise variables to allow compilation on Debian-stable, etc */
1659 int m1000E6 = 0;
1660 int i1000E6 = 0;
1661 int m1000E3 = 0;
1662 int i1000E3 = 0;
1663 int m1000 = 0;
1664 int i1000 = 0;
1665 int m100 = 0;
1666 int i100 = 0;
1668 if (i == 0 && rzad > 0) {
1669 return;
1671 if (i == 0) {
1672 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1673 return;
1676 m1000E6 = i % 1000000000;
1677 i1000E6 = i / 1000000000;
1679 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1681 m1000E3 = m1000E6 % 1000000;
1682 i1000E3 = m1000E6 / 1000000;
1684 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1686 m1000 = m1000E3 % 1000;
1687 i1000 = m1000E3 / 1000;
1689 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1691 m100 = m1000 % 100;
1692 i100 = m1000 / 100;
1694 if (i100>0)
1695 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1697 if ( m100 > 0 && m100 <=9 ) {
1698 if (m1000>0)
1699 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1700 else
1701 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1702 } else if (m100 % 10 == 0) {
1703 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1704 } else if (m100 <= 19 ) {
1705 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1706 } else if (m100 != 0) {
1707 if (odm->separator_dziesiatek[0]==' ') {
1708 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1709 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1710 } else {
1711 char buf[10];
1712 char *b = buf;
1713 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1714 b = pl_append(b, odm->separator_dziesiatek);
1715 b = pl_append(b, odm->cyfry2[m100 % 10]);
1716 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1720 if (rzad > 0) {
1721 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1725 /* ast_say_number_full_pl: Polish syntax */
1726 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)
1728 Sounds needed:
1729 0 zero
1730 1 jeden
1731 10 dziesiec
1732 100 sto
1733 1000 tysiac
1734 1000000 milion
1735 1000000000 miliard
1736 1000000000.2 miliardy
1737 1000000000.5 miliardow
1738 1000000.2 miliony
1739 1000000.5 milionow
1740 1000.2 tysiace
1741 1000.5 tysiecy
1742 100m stu
1743 10m dziesieciu
1744 11 jedenascie
1745 11m jedenastu
1746 12 dwanascie
1747 12m dwunastu
1748 13 trzynascie
1749 13m trzynastu
1750 14 czternascie
1751 14m czternastu
1752 15 pietnascie
1753 15m pietnastu
1754 16 szesnascie
1755 16m szesnastu
1756 17 siedemnascie
1757 17m siedemnastu
1758 18 osiemnascie
1759 18m osiemnastu
1760 19 dziewietnascie
1761 19m dziewietnastu
1762 1z jedna
1763 2 dwa
1764 20 dwadziescia
1765 200 dwiescie
1766 200m dwustu
1767 20m dwudziestu
1768 2-1m dwaj
1769 2-2m dwoch
1770 2z dwie
1771 3 trzy
1772 30 trzydziesci
1773 300 trzysta
1774 300m trzystu
1775 30m trzydziestu
1776 3-1m trzej
1777 3-2m trzech
1778 4 cztery
1779 40 czterdziesci
1780 400 czterysta
1781 400m czterystu
1782 40m czterdziestu
1783 4-1m czterej
1784 4-2m czterech
1785 5 piec
1786 50 piecdziesiat
1787 500 piecset
1788 500m pieciuset
1789 50m piedziesieciu
1790 5m pieciu
1791 6 szesc
1792 60 szescdziesiat
1793 600 szescset
1794 600m szesciuset
1795 60m szescdziesieciu
1796 6m szesciu
1797 7 siedem
1798 70 siedemdziesiat
1799 700 siedemset
1800 700m siedmiuset
1801 70m siedemdziesieciu
1802 7m siedmiu
1803 8 osiem
1804 80 osiemdziesiat
1805 800 osiemset
1806 800m osmiuset
1807 80m osiemdziesieciu
1808 8m osmiu
1809 9 dziewiec
1810 90 dziewiecdziesiat
1811 900 dziewiecset
1812 900m dziewieciuset
1813 90m dziewiedziesieciu
1814 9m dziewieciu
1815 and combinations of eg.: 20_1, 30m_3m, etc...
1819 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1821 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1823 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1825 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1827 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1829 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1831 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1833 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1835 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1837 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1839 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1841 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1843 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1845 /* Initialise variables to allow compilation on Debian-stable, etc */
1846 odmiana *o;
1848 static odmiana *odmiana_nieosobowa = NULL;
1849 static odmiana *odmiana_meska = NULL;
1850 static odmiana *odmiana_zenska = NULL;
1852 if (odmiana_nieosobowa == NULL) {
1853 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1855 odmiana_nieosobowa->separator_dziesiatek = " ";
1857 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1858 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1859 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1860 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1861 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1862 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1865 if (odmiana_zenska == NULL) {
1866 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1868 odmiana_zenska->separator_dziesiatek = " ";
1870 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1871 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1872 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1873 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1874 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1875 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1878 if (odmiana_meska == NULL) {
1879 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1881 odmiana_meska->separator_dziesiatek = " ";
1883 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1884 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1885 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1886 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1887 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1888 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1891 if (options) {
1892 if (strncasecmp(options, "f", 1) == 0)
1893 o = odmiana_zenska;
1894 else if (strncasecmp(options, "m", 1) == 0)
1895 o = odmiana_meska;
1896 else
1897 o = odmiana_nieosobowa;
1898 } else
1899 o = odmiana_nieosobowa;
1901 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1902 return 0;
1905 /* ast_say_number_full_pt: Portuguese syntax */
1906 /* Extra sounds needed: */
1907 /* For feminin all sound files end with F */
1908 /* 100E for 100+ something */
1909 /* 1000000S for plural */
1910 /* pt-e for 'and' */
1911 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)
1913 int res = 0;
1914 int playh = 0;
1915 int mf = 1; /* +1 = male; -1 = female */
1916 char fn[256] = "";
1918 if (!num)
1919 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1921 if (options && !strncasecmp(options, "f",1))
1922 mf = -1;
1924 while(!res && num ) {
1925 if (num < 0) {
1926 snprintf(fn, sizeof(fn), "digits/minus");
1927 if ( num > INT_MIN ) {
1928 num = -num;
1929 } else {
1930 num = 0;
1932 } else if (num < 20) {
1933 if ((num == 1 || num == 2) && (mf < 0))
1934 snprintf(fn, sizeof(fn), "digits/%dF", num);
1935 else
1936 snprintf(fn, sizeof(fn), "digits/%d", num);
1937 num = 0;
1938 } else if (num < 100) {
1939 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1940 if (num % 10)
1941 playh = 1;
1942 num = num % 10;
1943 } else if (num < 1000) {
1944 if (num == 100)
1945 snprintf(fn, sizeof(fn), "digits/100");
1946 else if (num < 200)
1947 snprintf(fn, sizeof(fn), "digits/100E");
1948 else {
1949 if (mf < 0 && num > 199)
1950 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1951 else
1952 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1953 if (num % 100)
1954 playh = 1;
1956 num = num % 100;
1957 } else if (num < 1000000) {
1958 if (num > 1999) {
1959 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1960 if (res)
1961 return res;
1963 snprintf(fn, sizeof(fn), "digits/1000");
1964 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1965 playh = 1;
1966 num = num % 1000;
1967 } else if (num < 1000000000) {
1968 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1969 if (res)
1970 return res;
1971 if (num < 2000000)
1972 snprintf(fn, sizeof(fn), "digits/1000000");
1973 else
1974 snprintf(fn, sizeof(fn), "digits/1000000S");
1976 if ((num % 1000000) &&
1977 /* no thousands */
1978 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1979 /* no hundreds and below */
1980 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1981 playh = 1;
1982 num = num % 1000000;
1984 if (!res) {
1985 if (!ast_streamfile(chan, fn, language)) {
1986 if ((audiofd > -1) && (ctrlfd > -1))
1987 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1988 else
1989 res = ast_waitstream(chan, ints);
1991 ast_stopstream(chan);
1993 if (!res && playh) {
1994 res = wait_file(chan, ints, "digits/pt-e", language);
1995 ast_stopstream(chan);
1996 playh = 0;
1999 return res;
2002 /*! \brief ast_say_number_full_se: Swedish syntax */
2003 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)
2005 int res = 0;
2006 int playh = 0;
2007 char fn[256] = "";
2008 int cn = 1; /* +1 = commune; -1 = neuter */
2009 if (!num)
2010 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2011 if (options && !strncasecmp(options, "n",1)) cn = -1;
2013 while(!res && (num || playh)) {
2014 if (num < 0) {
2015 snprintf(fn, sizeof(fn), "digits/minus");
2016 if ( num > INT_MIN ) {
2017 num = -num;
2018 } else {
2019 num = 0;
2021 } else if (playh) {
2022 snprintf(fn, sizeof(fn), "digits/hundred");
2023 playh = 0;
2024 } else if (num < 20) {
2025 snprintf(fn, sizeof(fn), "digits/%d", num);
2026 num = 0;
2027 } else if (num < 100) {
2028 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2029 num -= ((num / 10) * 10);
2030 } else if (num == 1 && cn == -1) { /* En eller ett? */
2031 snprintf(fn, sizeof(fn), "digits/1N");
2032 num = 0;
2033 } else {
2034 if (num < 1000){
2035 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2036 playh++;
2037 num -= ((num / 100) * 100);
2038 } else {
2039 if (num < 1000000) { /* 1,000,000 */
2040 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2041 if (res) {
2042 return res;
2044 num = num % 1000;
2045 snprintf(fn, sizeof(fn), "digits/thousand");
2046 } else {
2047 if (num < 1000000000) { /* 1,000,000,000 */
2048 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2049 if (res) {
2050 return res;
2052 num = num % 1000000;
2053 snprintf(fn, sizeof(fn), "digits/million");
2054 } else {
2055 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2056 res = -1;
2061 if (!res) {
2062 if(!ast_streamfile(chan, fn, language)) {
2063 if ((audiofd > -1) && (ctrlfd > -1))
2064 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2065 else
2066 res = ast_waitstream(chan, ints);
2067 ast_stopstream(chan);
2071 return res;
2074 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2075 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2077 int res = 0;
2078 int playh = 0;
2079 char fn[256] = "";
2080 if (!num)
2081 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2083 while(!res && (num || playh)) {
2084 if (num < 0) {
2085 snprintf(fn, sizeof(fn), "digits/minus");
2086 if ( num > INT_MIN ) {
2087 num = -num;
2088 } else {
2089 num = 0;
2091 } else if (playh) {
2092 snprintf(fn, sizeof(fn), "digits/hundred");
2093 playh = 0;
2094 } else if (num < 10) {
2095 snprintf(fn, sizeof(fn), "digits/%d", num);
2096 num = 0;
2097 } else if (num < 100) {
2098 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2099 num -= ((num / 10) * 10);
2100 } else {
2101 if (num < 1000){
2102 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2103 playh++;
2104 num -= ((num / 100) * 100);
2105 } else {
2106 if (num < 1000000) { /* 1,000,000 */
2107 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2108 if (res)
2109 return res;
2110 num = num % 1000;
2111 snprintf(fn, sizeof(fn), "digits/thousand");
2112 } else {
2113 if (num < 1000000000) { /* 1,000,000,000 */
2114 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2115 if (res)
2116 return res;
2117 num = num % 1000000;
2118 snprintf(fn, sizeof(fn), "digits/million");
2119 } else {
2120 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2121 res = -1;
2126 if (!res) {
2127 if(!ast_streamfile(chan, fn, language)) {
2128 if ((audiofd > -1) && (ctrlfd > -1))
2129 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2130 else
2131 res = ast_waitstream(chan, ints);
2133 ast_stopstream(chan);
2136 return res;
2140 /*! \brief determine last digits for thousands/millions (ru) */
2141 static int get_lastdigits_ru(int num) {
2142 if (num < 20) {
2143 return num;
2144 } else if (num < 100) {
2145 return get_lastdigits_ru(num % 10);
2146 } else if (num < 1000) {
2147 return get_lastdigits_ru(num % 100);
2149 return 0; /* number too big */
2153 /*! \brief ast_say_number_full_ru: Russian syntax */
2154 /*! \brief additional files:
2155 n00.gsm (one hundred, two hundred, ...)
2156 thousand.gsm
2157 million.gsm
2158 thousands-i.gsm (tisyachi)
2159 million-a.gsm (milliona)
2160 thousands.gsm
2161 millions.gsm
2162 1f.gsm (odna)
2163 2f.gsm (dve)
2165 where 'n' from 1 to 9
2167 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)
2169 int res = 0;
2170 int lastdigits = 0;
2171 char fn[256] = "";
2172 if (!num)
2173 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2175 while(!res && (num)) {
2176 if (num < 0) {
2177 snprintf(fn, sizeof(fn), "digits/minus");
2178 if ( num > INT_MIN ) {
2179 num = -num;
2180 } else {
2181 num = 0;
2183 } else if (num < 20) {
2184 if(options && strlen(options) == 1 && num < 3) {
2185 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2186 } else {
2187 snprintf(fn, sizeof(fn), "digits/%d", num);
2189 num = 0;
2190 } else if (num < 100) {
2191 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2192 num %= 10;
2193 } else if (num < 1000){
2194 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2195 num %= 100;
2196 } else if (num < 1000000) { /* 1,000,000 */
2197 lastdigits = get_lastdigits_ru(num / 1000);
2198 /* say thousands */
2199 if (lastdigits < 3) {
2200 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2201 } else {
2202 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2204 if (res)
2205 return res;
2206 if (lastdigits == 1) {
2207 snprintf(fn, sizeof(fn), "digits/thousand");
2208 } else if (lastdigits > 1 && lastdigits < 5) {
2209 snprintf(fn, sizeof(fn), "digits/thousands-i");
2210 } else {
2211 snprintf(fn, sizeof(fn), "digits/thousands");
2213 num %= 1000;
2214 } else if (num < 1000000000) { /* 1,000,000,000 */
2215 lastdigits = get_lastdigits_ru(num / 1000000);
2216 /* say millions */
2217 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2218 if (res)
2219 return res;
2220 if (lastdigits == 1) {
2221 snprintf(fn, sizeof(fn), "digits/million");
2222 } else if (lastdigits > 1 && lastdigits < 5) {
2223 snprintf(fn, sizeof(fn), "digits/million-a");
2224 } else {
2225 snprintf(fn, sizeof(fn), "digits/millions");
2227 num %= 1000000;
2228 } else {
2229 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2230 res = -1;
2232 if (!res) {
2233 if (!ast_streamfile(chan, fn, language)) {
2234 if ((audiofd > -1) && (ctrlfd > -1))
2235 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2236 else
2237 res = ast_waitstream(chan, ints);
2239 ast_stopstream(chan);
2242 return res;
2246 /*! \brief ast_say_enumeration_full: call language-specific functions */
2247 /* Called from AGI */
2248 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2250 if (!strcasecmp(language,"en") ) { /* English syntax */
2251 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2252 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2253 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2254 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2255 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2258 /* Default to english */
2259 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2262 /*! \brief ast_say_enumeration_full_en: English syntax */
2263 /* This is the default syntax, if no other syntax defined in this file is used */
2264 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2266 int res = 0, t = 0;
2267 char fn[256] = "";
2269 while(!res && num) {
2270 if (num < 0) {
2271 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2272 if ( num > INT_MIN ) {
2273 num = -num;
2274 } else {
2275 num = 0;
2277 } else if (num < 20) {
2278 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2279 num = 0;
2280 } else if (num < 100) {
2281 int tens = num / 10;
2282 num = num % 10;
2283 if (num == 0) {
2284 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2285 } else {
2286 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2288 } else if (num < 1000) {
2289 int hundreds = num / 100;
2290 num = num % 100;
2291 if (hundreds > 1 || t == 1) {
2292 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2294 if (res)
2295 return res;
2296 if (num) {
2297 snprintf(fn, sizeof(fn), "digits/hundred");
2298 } else {
2299 snprintf(fn, sizeof(fn), "digits/h-hundred");
2301 } else if (num < 1000000) {
2302 int thousands = num / 1000;
2303 num = num % 1000;
2304 if (thousands > 1 || t == 1) {
2305 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2307 if (res)
2308 return res;
2309 if (num) {
2310 snprintf(fn, sizeof(fn), "digits/thousand");
2311 } else {
2312 snprintf(fn, sizeof(fn), "digits/h-thousand");
2314 t = 1;
2315 } else if (num < 1000000000) {
2316 int millions = num / 1000000;
2317 num = num % 1000000;
2318 t = 1;
2319 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2320 if (res)
2321 return res;
2322 if (num) {
2323 snprintf(fn, sizeof(fn), "digits/million");
2324 } else {
2325 snprintf(fn, sizeof(fn), "digits/h-million");
2327 } else if (num < INT_MAX) {
2328 int billions = num / 1000000000;
2329 num = num % 1000000000;
2330 t = 1;
2331 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2332 if (res)
2333 return res;
2334 if (num) {
2335 snprintf(fn, sizeof(fn), "digits/billion");
2336 } else {
2337 snprintf(fn, sizeof(fn), "digits/h-billion");
2339 } else if (num == INT_MAX) {
2340 snprintf(fn, sizeof(fn), "digits/h-last");
2341 num = 0;
2342 } else {
2343 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2344 res = -1;
2347 if (!res) {
2348 if (!ast_streamfile(chan, fn, language)) {
2349 if ((audiofd > -1) && (ctrlfd > -1)) {
2350 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2351 } else {
2352 res = ast_waitstream(chan, ints);
2355 ast_stopstream(chan);
2358 return res;
2361 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2362 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)
2364 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2365 int res = 0, t = 0;
2366 char fn[256] = "", fna[256] = "";
2367 char *gender;
2369 if (options && !strncasecmp(options, "f",1)) {
2370 gender = "F";
2371 } else if (options && !strncasecmp(options, "n",1)) {
2372 gender = "N";
2373 } else {
2374 gender = "";
2377 if (!num)
2378 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2380 while(!res && num) {
2381 if (num < 0) {
2382 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2383 if ( num > INT_MIN ) {
2384 num = -num;
2385 } else {
2386 num = 0;
2388 } else if (num < 100 && t) {
2389 snprintf(fn, sizeof(fn), "digits/and");
2390 t = 0;
2391 } else if (num < 20) {
2392 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2393 num = 0;
2394 } else if (num < 100) {
2395 int ones = num % 10;
2396 if (ones) {
2397 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2398 num -= ones;
2399 } else {
2400 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2401 num = 0;
2403 } else if (num == 100 && t == 0) {
2404 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2405 num = 0;
2406 } else if (num < 1000) {
2407 int hundreds = num / 100;
2408 num = num % 100;
2409 if (hundreds == 1) {
2410 snprintf(fn, sizeof(fn), "digits/1N");
2411 } else {
2412 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2414 if (num) {
2415 snprintf(fna, sizeof(fna), "digits/hundred");
2416 } else {
2417 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2419 t = 1;
2420 } else if (num < 1000000) {
2421 int thousands = num / 1000;
2422 num = num % 1000;
2423 if (thousands == 1) {
2424 if (num) {
2425 snprintf(fn, sizeof(fn), "digits/1N");
2426 snprintf(fna, sizeof(fna), "digits/thousand");
2427 } else {
2428 if (t) {
2429 snprintf(fn, sizeof(fn), "digits/1N");
2430 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2431 } else {
2432 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2435 } else {
2436 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2437 if (res) {
2438 return res;
2440 if (num) {
2441 snprintf(fn, sizeof(fn), "digits/thousand");
2442 } else {
2443 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2446 t = 1;
2447 } else if (num < 1000000000) {
2448 int millions = num / 1000000;
2449 num = num % 1000000;
2450 if (millions == 1) {
2451 if (num) {
2452 snprintf(fn, sizeof(fn), "digits/1F");
2453 snprintf(fna, sizeof(fna), "digits/million");
2454 } else {
2455 snprintf(fn, sizeof(fn), "digits/1N");
2456 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2458 } else {
2459 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2460 if (res) {
2461 return res;
2463 if (num) {
2464 snprintf(fn, sizeof(fn), "digits/millions");
2465 } else {
2466 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2469 t = 1;
2470 } else if (num < INT_MAX) {
2471 int billions = num / 1000000000;
2472 num = num % 1000000000;
2473 if (billions == 1) {
2474 if (num) {
2475 snprintf(fn, sizeof(fn), "digits/1F");
2476 snprintf(fna, sizeof(fna), "digits/milliard");
2477 } else {
2478 snprintf(fn, sizeof(fn), "digits/1N");
2479 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2481 } else {
2482 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2483 if (res)
2484 return res;
2485 if (num) {
2486 snprintf(fn, sizeof(fna), "digits/milliards");
2487 } else {
2488 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2491 t = 1;
2492 } else if (num == INT_MAX) {
2493 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2494 num = 0;
2495 } else {
2496 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2497 res = -1;
2500 if (!res) {
2501 if (!ast_streamfile(chan, fn, language)) {
2502 if ((audiofd > -1) && (ctrlfd > -1))
2503 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2504 else
2505 res = ast_waitstream(chan, ints);
2507 ast_stopstream(chan);
2508 if (!res) {
2509 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2510 if ((audiofd > -1) && (ctrlfd > -1)) {
2511 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2512 } else {
2513 res = ast_waitstream(chan, ints);
2516 ast_stopstream(chan);
2517 strcpy(fna, "");
2521 return res;
2524 /*! \brief ast_say_enumeration_full_de: German syntax */
2525 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)
2527 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2528 int res = 0, t = 0;
2529 char fn[256] = "", fna[256] = "";
2530 char *gender;
2532 if (options && !strncasecmp(options, "f",1)) {
2533 gender = "F";
2534 } else if (options && !strncasecmp(options, "n",1)) {
2535 gender = "N";
2536 } else {
2537 gender = "";
2540 if (!num)
2541 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2543 while(!res && num) {
2544 if (num < 0) {
2545 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2546 if ( num > INT_MIN ) {
2547 num = -num;
2548 } else {
2549 num = 0;
2551 } else if (num < 100 && t) {
2552 snprintf(fn, sizeof(fn), "digits/and");
2553 t = 0;
2554 } else if (num < 20) {
2555 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2556 num = 0;
2557 } else if (num < 100) {
2558 int ones = num % 10;
2559 if (ones) {
2560 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2561 num -= ones;
2562 } else {
2563 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2564 num = 0;
2566 } else if (num == 100 && t == 0) {
2567 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2568 num = 0;
2569 } else if (num < 1000) {
2570 int hundreds = num / 100;
2571 num = num % 100;
2572 if (hundreds == 1) {
2573 snprintf(fn, sizeof(fn), "digits/1N");
2574 } else {
2575 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2577 if (num) {
2578 snprintf(fna, sizeof(fna), "digits/hundred");
2579 } else {
2580 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2582 t = 1;
2583 } else if (num < 1000000) {
2584 int thousands = num / 1000;
2585 num = num % 1000;
2586 if (thousands == 1) {
2587 if (num) {
2588 snprintf(fn, sizeof(fn), "digits/1N");
2589 snprintf(fna, sizeof(fna), "digits/thousand");
2590 } else {
2591 if (t) {
2592 snprintf(fn, sizeof(fn), "digits/1N");
2593 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2594 } else {
2595 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2598 } else {
2599 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2600 if (res) {
2601 return res;
2603 if (num) {
2604 snprintf(fn, sizeof(fn), "digits/thousand");
2605 } else {
2606 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2609 t = 1;
2610 } else if (num < 1000000000) {
2611 int millions = num / 1000000;
2612 num = num % 1000000;
2613 if (millions == 1) {
2614 if (num) {
2615 snprintf(fn, sizeof(fn), "digits/1F");
2616 snprintf(fna, sizeof(fna), "digits/million");
2617 } else {
2618 snprintf(fn, sizeof(fn), "digits/1N");
2619 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2621 } else {
2622 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2623 if (res) {
2624 return res;
2626 if (num) {
2627 snprintf(fn, sizeof(fn), "digits/millions");
2628 } else {
2629 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2632 t = 1;
2633 } else if (num < INT_MAX) {
2634 int billions = num / 1000000000;
2635 num = num % 1000000000;
2636 if (billions == 1) {
2637 if (num) {
2638 snprintf(fn, sizeof(fn), "digits/1F");
2639 snprintf(fna, sizeof(fna), "digits/milliard");
2640 } else {
2641 snprintf(fn, sizeof(fn), "digits/1N");
2642 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2644 } else {
2645 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2646 if (res)
2647 return res;
2648 if (num) {
2649 snprintf(fn, sizeof(fna), "digits/milliards");
2650 } else {
2651 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2654 t = 1;
2655 } else if (num == INT_MAX) {
2656 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2657 num = 0;
2658 } else {
2659 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2660 res = -1;
2663 if (!res) {
2664 if (!ast_streamfile(chan, fn, language)) {
2665 if ((audiofd > -1) && (ctrlfd > -1))
2666 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2667 else
2668 res = ast_waitstream(chan, ints);
2670 ast_stopstream(chan);
2671 if (!res) {
2672 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2673 if ((audiofd > -1) && (ctrlfd > -1)) {
2674 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2675 } else {
2676 res = ast_waitstream(chan, ints);
2679 ast_stopstream(chan);
2680 strcpy(fna, "");
2684 return res;
2687 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2689 if (!strcasecmp(lang, "en") ) { /* English syntax */
2690 return(ast_say_date_en(chan, t, ints, lang));
2691 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2692 return(ast_say_date_da(chan, t, ints, lang));
2693 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2694 return(ast_say_date_de(chan, t, ints, lang));
2695 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2696 return(ast_say_date_fr(chan, t, ints, lang));
2697 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2698 return(ast_say_date_nl(chan, t, ints, lang));
2699 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2700 return(ast_say_date_pt(chan, t, ints, lang));
2701 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2702 return(ast_say_date_gr(chan, t, ints, lang));
2705 /* Default to English */
2706 return(ast_say_date_en(chan, t, ints, lang));
2709 /* English syntax */
2710 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2712 struct tm tm;
2713 char fn[256];
2714 int res = 0;
2715 ast_localtime(&t,&tm,NULL);
2716 if (!res) {
2717 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2718 res = ast_streamfile(chan, fn, lang);
2719 if (!res)
2720 res = ast_waitstream(chan, ints);
2722 if (!res) {
2723 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2724 res = ast_streamfile(chan, fn, lang);
2725 if (!res)
2726 res = ast_waitstream(chan, ints);
2728 if (!res)
2729 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2730 if (!res)
2731 res = ast_waitstream(chan, ints);
2732 if (!res)
2733 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2734 return res;
2737 /* Danish syntax */
2738 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2740 struct tm tm;
2741 char fn[256];
2742 int res = 0;
2743 ast_localtime(&t,&tm,NULL);
2744 if (!res) {
2745 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2746 res = ast_streamfile(chan, fn, lang);
2747 if (!res)
2748 res = ast_waitstream(chan, ints);
2750 if (!res)
2751 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2752 if (!res)
2753 res = ast_waitstream(chan, ints);
2754 if (!res) {
2755 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2756 res = ast_streamfile(chan, fn, lang);
2757 if (!res)
2758 res = ast_waitstream(chan, ints);
2760 if (!res) {
2761 /* Year */
2762 int year = tm.tm_year + 1900;
2763 if (year > 1999) { /* year 2000 and later */
2764 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2765 } else {
2766 if (year < 1100) {
2767 /* I'm not going to handle 1100 and prior */
2768 /* We'll just be silent on the year, instead of bombing out. */
2769 } else {
2770 /* year 1100 to 1999. will anybody need this?!? */
2771 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2772 res = wait_file(chan, ints, fn, lang);
2773 if (!res) {
2774 res = wait_file(chan,ints, "digits/hundred", lang);
2775 if (!res && year % 100 != 0) {
2776 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2782 return res;
2785 /* German syntax */
2786 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2788 struct tm tm;
2789 char fn[256];
2790 int res = 0;
2791 ast_localtime(&t,&tm,NULL);
2792 if (!res) {
2793 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2794 res = ast_streamfile(chan, fn, lang);
2795 if (!res)
2796 res = ast_waitstream(chan, ints);
2798 if (!res)
2799 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2800 if (!res)
2801 res = ast_waitstream(chan, ints);
2802 if (!res) {
2803 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2804 res = ast_streamfile(chan, fn, lang);
2805 if (!res)
2806 res = ast_waitstream(chan, ints);
2808 if (!res) {
2809 /* Year */
2810 int year = tm.tm_year + 1900;
2811 if (year > 1999) { /* year 2000 and later */
2812 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2813 } else {
2814 if (year < 1100) {
2815 /* I'm not going to handle 1100 and prior */
2816 /* We'll just be silent on the year, instead of bombing out. */
2817 } else {
2818 /* year 1100 to 1999. will anybody need this?!? */
2819 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2820 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2821 res = wait_file(chan, ints, fn, lang);
2822 if (!res) {
2823 res = wait_file(chan,ints, "digits/hundred", lang);
2824 if (!res && year % 100 != 0) {
2825 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2831 return res;
2834 /* French syntax */
2835 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2837 struct tm tm;
2838 char fn[256];
2839 int res = 0;
2840 ast_localtime(&t,&tm,NULL);
2841 if (!res) {
2842 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2843 res = ast_streamfile(chan, fn, lang);
2844 if (!res)
2845 res = ast_waitstream(chan, ints);
2847 if (!res)
2848 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2849 if (!res)
2850 res = ast_waitstream(chan, ints);
2851 if (!res) {
2852 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2853 res = ast_streamfile(chan, fn, lang);
2854 if (!res)
2855 res = ast_waitstream(chan, ints);
2857 if (!res)
2858 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2859 return res;
2862 /* Dutch syntax */
2863 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2865 struct tm tm;
2866 char fn[256];
2867 int res = 0;
2868 ast_localtime(&t,&tm,NULL);
2869 if (!res) {
2870 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2871 res = ast_streamfile(chan, fn, lang);
2872 if (!res)
2873 res = ast_waitstream(chan, ints);
2875 if (!res)
2876 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2877 if (!res) {
2878 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2879 res = ast_streamfile(chan, fn, lang);
2880 if (!res)
2881 res = ast_waitstream(chan, ints);
2883 if (!res)
2884 res = ast_waitstream(chan, ints);
2885 if (!res)
2886 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2887 return res;
2890 /* Portuguese syntax */
2891 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2893 struct tm tm;
2894 char fn[256];
2895 int res = 0;
2896 ast_localtime(&t,&tm,NULL);
2897 localtime_r(&t,&tm);
2898 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2899 if (!res)
2900 res = wait_file(chan, ints, fn, lang);
2901 if (!res)
2902 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2903 if (!res)
2904 res = wait_file(chan, ints, "digits/pt-de", lang);
2905 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2906 if (!res)
2907 res = wait_file(chan, ints, fn, lang);
2908 if (!res)
2909 res = wait_file(chan, ints, "digits/pt-de", lang);
2910 if (!res)
2911 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2913 return res;
2916 static int say_date_with_format(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
2918 if (!strcasecmp(lang, "en") ) { /* English syntax */
2919 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2920 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2921 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
2922 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2923 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
2924 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
2925 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
2926 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2927 return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
2928 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2929 return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
2930 } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
2931 return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
2932 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2933 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
2934 } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
2935 return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
2936 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2937 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
2938 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
2939 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
2940 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2941 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
2944 /* Default to English */
2945 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2948 /* English syntax */
2949 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
2951 struct tm tm;
2952 int res=0, offset, sndoffset;
2953 char sndfile[256], nextmsg[256];
2955 if (format == NULL)
2956 format = "ABdY 'digits/at' IMp";
2958 ast_localtime(&time,&tm,timezone);
2960 for (offset=0 ; format[offset] != '\0' ; offset++) {
2961 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2962 switch (format[offset]) {
2963 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2964 case '\'':
2965 /* Literal name of a sound file */
2966 sndoffset=0;
2967 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2968 sndfile[sndoffset] = format[offset];
2969 sndfile[sndoffset] = '\0';
2970 res = wait_file(chan,ints,sndfile,lang);
2971 break;
2972 case 'A':
2973 case 'a':
2974 /* Sunday - Saturday */
2975 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2976 res = wait_file(chan,ints,nextmsg,lang);
2977 break;
2978 case 'B':
2979 case 'b':
2980 case 'h':
2981 /* January - December */
2982 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2983 res = wait_file(chan,ints,nextmsg,lang);
2984 break;
2985 case 'm':
2986 /* Month enumerated */
2987 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
2988 break;
2989 case 'd':
2990 case 'e':
2991 /* First - Thirtyfirst */
2992 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
2993 break;
2994 case 'Y':
2995 /* Year */
2996 if (tm.tm_year > 99) {
2997 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2998 } else if (tm.tm_year < 1) {
2999 /* I'm not going to handle 1900 and prior */
3000 /* We'll just be silent on the year, instead of bombing out. */
3001 } else {
3002 res = wait_file(chan, ints, "digits/19", lang);
3003 if (!res) {
3004 if (tm.tm_year <= 9) {
3005 /* 1901 - 1909 */
3006 res = wait_file(chan,ints, "digits/oh", lang);
3009 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3012 break;
3013 case 'I':
3014 case 'l':
3015 /* 12-Hour */
3016 if (tm.tm_hour == 0)
3017 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3018 else if (tm.tm_hour > 12)
3019 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3020 else
3021 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3022 res = wait_file(chan,ints,nextmsg,lang);
3023 break;
3024 case 'H':
3025 case 'k':
3026 /* 24-Hour */
3027 if (format[offset] == 'H') {
3028 /* e.g. oh-eight */
3029 if (tm.tm_hour < 10) {
3030 res = wait_file(chan,ints, "digits/oh",lang);
3032 } else {
3033 /* e.g. eight */
3034 if (tm.tm_hour == 0) {
3035 res = wait_file(chan,ints, "digits/oh",lang);
3038 if (!res) {
3039 if (tm.tm_hour != 0) {
3040 int remainder = tm.tm_hour;
3041 if (tm.tm_hour > 20) {
3042 res = wait_file(chan,ints, "digits/20",lang);
3043 remainder -= 20;
3045 if (!res) {
3046 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3047 res = wait_file(chan,ints,nextmsg,lang);
3051 break;
3052 case 'M':
3053 case 'N':
3054 /* Minute */
3055 if (tm.tm_min == 0) {
3056 if (format[offset] == 'M') {
3057 res = wait_file(chan, ints, "digits/oclock", lang);
3058 } else {
3059 res = wait_file(chan, ints, "digits/hundred", lang);
3061 } else if (tm.tm_min < 10) {
3062 res = wait_file(chan,ints, "digits/oh",lang);
3063 if (!res) {
3064 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3065 res = wait_file(chan,ints,nextmsg,lang);
3067 } else {
3068 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3070 break;
3071 case 'P':
3072 case 'p':
3073 /* AM/PM */
3074 if (tm.tm_hour > 11)
3075 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3076 else
3077 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3078 res = wait_file(chan,ints,nextmsg,lang);
3079 break;
3080 case 'Q':
3081 /* Shorthand for "Today", "Yesterday", or ABdY */
3082 /* XXX As emphasized elsewhere, this should the native way in your
3083 * language to say the date, with changes in what you say, depending
3084 * upon how recent the date is. XXX */
3086 struct timeval now;
3087 struct tm tmnow;
3088 time_t beg_today, tt;
3090 gettimeofday(&now,NULL);
3091 tt = now.tv_sec;
3092 ast_localtime(&tt,&tmnow,timezone);
3093 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3094 /* In any case, it saves not having to do ast_mktime() */
3095 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3096 if (beg_today < time) {
3097 /* Today */
3098 res = wait_file(chan,ints, "digits/today",lang);
3099 } else if (beg_today - 86400 < time) {
3100 /* Yesterday */
3101 res = wait_file(chan,ints, "digits/yesterday",lang);
3102 } else if (beg_today - 86400 * 6 < time) {
3103 /* Within the last week */
3104 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3105 } else if (beg_today - 2628000 < time) {
3106 /* Less than a month ago - "Sunday, October third" */
3107 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3108 } else if (beg_today - 15768000 < time) {
3109 /* Less than 6 months ago - "August seventh" */
3110 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3111 } else {
3112 /* More than 6 months ago - "April nineteenth two thousand three" */
3113 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3116 break;
3117 case 'q':
3118 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3119 /* XXX As emphasized elsewhere, this should the native way in your
3120 * language to say the date, with changes in what you say, depending
3121 * upon how recent the date is. XXX */
3123 struct timeval now;
3124 struct tm tmnow;
3125 time_t beg_today, tt;
3127 gettimeofday(&now,NULL);
3128 tt = now.tv_sec;
3129 ast_localtime(&tt,&tmnow,timezone);
3130 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3131 /* In any case, it saves not having to do ast_mktime() */
3132 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3133 if (beg_today < time) {
3134 /* Today */
3135 } else if ((beg_today - 86400) < time) {
3136 /* Yesterday */
3137 res = wait_file(chan,ints, "digits/yesterday",lang);
3138 } else if (beg_today - 86400 * 6 < time) {
3139 /* Within the last week */
3140 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3141 } else if (beg_today - 2628000 < time) {
3142 /* Less than a month ago - "Sunday, October third" */
3143 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3144 } else if (beg_today - 15768000 < time) {
3145 /* Less than 6 months ago - "August seventh" */
3146 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3147 } else {
3148 /* More than 6 months ago - "April nineteenth two thousand three" */
3149 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3152 break;
3153 case 'R':
3154 res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
3155 break;
3156 case 'S':
3157 /* Seconds */
3158 if (tm.tm_sec == 0) {
3159 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3160 res = wait_file(chan,ints,nextmsg,lang);
3161 } else if (tm.tm_sec < 10) {
3162 res = wait_file(chan,ints, "digits/oh",lang);
3163 if (!res) {
3164 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3165 res = wait_file(chan,ints,nextmsg,lang);
3167 } else {
3168 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
3170 break;
3171 case 'T':
3172 res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
3173 break;
3174 case ' ':
3175 case ' ':
3176 /* Just ignore spaces and tabs */
3177 break;
3178 default:
3179 /* Unknown character */
3180 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3182 /* Jump out on DTMF */
3183 if (res) {
3184 break;
3187 return res;
3190 /* Danish syntax */
3191 int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3193 struct tm tm;
3194 int res=0, offset, sndoffset;
3195 char sndfile[256], nextmsg[256];
3197 if (!format)
3198 format = "A dBY HMS";
3200 ast_localtime(&time,&tm,timezone);
3202 for (offset=0 ; format[offset] != '\0' ; offset++) {
3203 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3204 switch (format[offset]) {
3205 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3206 case '\'':
3207 /* Literal name of a sound file */
3208 sndoffset=0;
3209 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3210 sndfile[sndoffset] = format[offset];
3211 sndfile[sndoffset] = '\0';
3212 res = wait_file(chan,ints,sndfile,lang);
3213 break;
3214 case 'A':
3215 case 'a':
3216 /* Sunday - Saturday */
3217 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3218 res = wait_file(chan,ints,nextmsg,lang);
3219 break;
3220 case 'B':
3221 case 'b':
3222 case 'h':
3223 /* January - December */
3224 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3225 res = wait_file(chan,ints,nextmsg,lang);
3226 break;
3227 case 'm':
3228 /* Month enumerated */
3229 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3230 break;
3231 case 'd':
3232 case 'e':
3233 /* First - Thirtyfirst */
3234 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3235 break;
3236 case 'Y':
3237 /* Year */
3239 int year = tm.tm_year + 1900;
3240 if (year > 1999) { /* year 2000 and later */
3241 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3242 } else {
3243 if (year < 1100) {
3244 /* I'm not going to handle 1100 and prior */
3245 /* We'll just be silent on the year, instead of bombing out. */
3246 } else {
3247 /* year 1100 to 1999. will anybody need this?!? */
3248 /* say 1967 as 'nineteen hundred seven and sixty' */
3249 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3250 res = wait_file(chan,ints,nextmsg,lang);
3251 if (!res) {
3252 res = wait_file(chan,ints, "digits/hundred",lang);
3253 if (!res && year % 100 != 0) {
3254 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3260 break;
3261 case 'I':
3262 case 'l':
3263 /* 12-Hour */
3264 res = wait_file(chan,ints,"digits/oclock",lang);
3265 if (tm.tm_hour == 0)
3266 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3267 else if (tm.tm_hour > 12)
3268 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3269 else
3270 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3271 if (!res) {
3272 res = wait_file(chan,ints,nextmsg,lang);
3274 break;
3275 case 'H':
3276 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3277 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
3278 res = wait_file(chan,ints, "digits/0",lang);
3280 /* FALLTRHU */
3281 case 'k':
3282 /* 24-Hour */
3283 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3284 break;
3285 case 'M':
3286 /* Minute */
3287 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3288 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3290 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3291 if (tm.tm_min == 1) {
3292 res = wait_file(chan,ints,"digits/minute",lang);
3293 } else {
3294 res = wait_file(chan,ints,"digits/minutes",lang);
3297 break;
3298 case 'P':
3299 case 'p':
3300 /* AM/PM */
3301 if (tm.tm_hour > 11)
3302 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3303 else
3304 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3305 res = wait_file(chan,ints,nextmsg,lang);
3306 break;
3307 case 'Q':
3308 /* Shorthand for "Today", "Yesterday", or AdBY */
3309 /* XXX As emphasized elsewhere, this should the native way in your
3310 * language to say the date, with changes in what you say, depending
3311 * upon how recent the date is. XXX */
3313 struct timeval now;
3314 struct tm tmnow;
3315 time_t beg_today, tt;
3317 gettimeofday(&now,NULL);
3318 tt = now.tv_sec;
3319 ast_localtime(&tt,&tmnow,timezone);
3320 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3321 /* In any case, it saves not having to do ast_mktime() */
3322 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3323 if (beg_today < time) {
3324 /* Today */
3325 res = wait_file(chan,ints, "digits/today",lang);
3326 } else if (beg_today - 86400 < time) {
3327 /* Yesterday */
3328 res = wait_file(chan,ints, "digits/yesterday",lang);
3329 } else {
3330 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3333 break;
3334 case 'q':
3335 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3336 /* XXX As emphasized elsewhere, this should the native way in your
3337 * language to say the date, with changes in what you say, depending
3338 * upon how recent the date is. XXX */
3340 struct timeval now;
3341 struct tm tmnow;
3342 time_t beg_today, tt;
3344 gettimeofday(&now,NULL);
3345 tt = now.tv_sec;
3346 ast_localtime(&tt,&tmnow,timezone);
3347 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3348 /* In any case, it saves not having to do ast_mktime() */
3349 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3350 if (beg_today < time) {
3351 /* Today */
3352 } else if ((beg_today - 86400) < time) {
3353 /* Yesterday */
3354 res = wait_file(chan,ints, "digits/yesterday",lang);
3355 } else if (beg_today - 86400 * 6 < time) {
3356 /* Within the last week */
3357 res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
3358 } else {
3359 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3362 break;
3363 case 'R':
3364 res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
3365 break;
3366 case 'S':
3367 /* Seconds */
3368 res = wait_file(chan,ints, "digits/and",lang);
3369 if (!res) {
3370 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3371 if (!res) {
3372 res = wait_file(chan,ints, "digits/seconds",lang);
3375 break;
3376 case 'T':
3377 res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
3378 break;
3379 case ' ':
3380 case ' ':
3381 /* Just ignore spaces and tabs */
3382 break;
3383 default:
3384 /* Unknown character */
3385 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3387 /* Jump out on DTMF */
3388 if (res) {
3389 break;
3392 return res;
3395 /* German syntax */
3396 int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3398 struct tm tm;
3399 int res=0, offset, sndoffset;
3400 char sndfile[256], nextmsg[256];
3402 if (!format)
3403 format = "A dBY HMS";
3405 ast_localtime(&time,&tm,timezone);
3407 for (offset=0 ; format[offset] != '\0' ; offset++) {
3408 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3409 switch (format[offset]) {
3410 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3411 case '\'':
3412 /* Literal name of a sound file */
3413 sndoffset=0;
3414 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3415 sndfile[sndoffset] = format[offset];
3416 sndfile[sndoffset] = '\0';
3417 res = wait_file(chan,ints,sndfile,lang);
3418 break;
3419 case 'A':
3420 case 'a':
3421 /* Sunday - Saturday */
3422 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3423 res = wait_file(chan,ints,nextmsg,lang);
3424 break;
3425 case 'B':
3426 case 'b':
3427 case 'h':
3428 /* January - December */
3429 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3430 res = wait_file(chan,ints,nextmsg,lang);
3431 break;
3432 case 'm':
3433 /* Month enumerated */
3434 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3435 break;
3436 case 'd':
3437 case 'e':
3438 /* First - Thirtyfirst */
3439 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3440 break;
3441 case 'Y':
3442 /* Year */
3444 int year = tm.tm_year + 1900;
3445 if (year > 1999) { /* year 2000 and later */
3446 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3447 } else {
3448 if (year < 1100) {
3449 /* I'm not going to handle 1100 and prior */
3450 /* We'll just be silent on the year, instead of bombing out. */
3451 } else {
3452 /* year 1100 to 1999. will anybody need this?!? */
3453 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3454 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3455 res = wait_file(chan,ints,nextmsg,lang);
3456 if (!res) {
3457 res = wait_file(chan,ints, "digits/hundred",lang);
3458 if (!res && year % 100 != 0) {
3459 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3465 break;
3466 case 'I':
3467 case 'l':
3468 /* 12-Hour */
3469 if (tm.tm_hour == 0)
3470 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3471 else if (tm.tm_hour > 12)
3472 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3473 else
3474 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3475 res = wait_file(chan,ints,nextmsg,lang);
3476 if (!res) {
3477 res = wait_file(chan,ints,"digits/oclock",lang);
3479 break;
3480 case 'H':
3481 case 'k':
3482 /* 24-Hour */
3483 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3484 if (!res) {
3485 res = wait_file(chan,ints,"digits/oclock",lang);
3487 break;
3488 case 'M':
3489 /* Minute */
3490 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3491 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3493 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3494 if (tm.tm_min == 1) {
3495 res = wait_file(chan,ints,"digits/minute",lang);
3496 } else {
3497 res = wait_file(chan,ints,"digits/minutes",lang);
3500 break;
3501 case 'P':
3502 case 'p':
3503 /* AM/PM */
3504 if (tm.tm_hour > 11)
3505 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3506 else
3507 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3508 res = wait_file(chan,ints,nextmsg,lang);
3509 break;
3510 case 'Q':
3511 /* Shorthand for "Today", "Yesterday", or AdBY */
3512 /* XXX As emphasized elsewhere, this should the native way in your
3513 * language to say the date, with changes in what you say, depending
3514 * upon how recent the date is. XXX */
3516 struct timeval now;
3517 struct tm tmnow;
3518 time_t beg_today, tt;
3520 gettimeofday(&now,NULL);
3521 tt = now.tv_sec;
3522 ast_localtime(&tt,&tmnow,timezone);
3523 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3524 /* In any case, it saves not having to do ast_mktime() */
3525 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3526 if (beg_today < time) {
3527 /* Today */
3528 res = wait_file(chan,ints, "digits/today",lang);
3529 } else if (beg_today - 86400 < time) {
3530 /* Yesterday */
3531 res = wait_file(chan,ints, "digits/yesterday",lang);
3532 } else {
3533 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3536 break;
3537 case 'q':
3538 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3539 /* XXX As emphasized elsewhere, this should the native way in your
3540 * language to say the date, with changes in what you say, depending
3541 * upon how recent the date is. XXX */
3543 struct timeval now;
3544 struct tm tmnow;
3545 time_t beg_today, tt;
3547 gettimeofday(&now,NULL);
3548 tt = now.tv_sec;
3549 ast_localtime(&tt,&tmnow,timezone);
3550 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3551 /* In any case, it saves not having to do ast_mktime() */
3552 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3553 if (beg_today < time) {
3554 /* Today */
3555 } else if ((beg_today - 86400) < time) {
3556 /* Yesterday */
3557 res = wait_file(chan,ints, "digits/yesterday",lang);
3558 } else if (beg_today - 86400 * 6 < time) {
3559 /* Within the last week */
3560 res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
3561 } else {
3562 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3565 break;
3566 case 'R':
3567 res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
3568 break;
3569 case 'S':
3570 /* Seconds */
3571 res = wait_file(chan,ints, "digits/and",lang);
3572 if (!res) {
3573 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3574 if (!res) {
3575 res = wait_file(chan,ints, "digits/seconds",lang);
3578 break;
3579 case 'T':
3580 res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
3581 break;
3582 case ' ':
3583 case ' ':
3584 /* Just ignore spaces and tabs */
3585 break;
3586 default:
3587 /* Unknown character */
3588 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3590 /* Jump out on DTMF */
3591 if (res) {
3592 break;
3595 return res;
3598 /* TODO: this probably is not the correct format for doxygen remarks */
3600 /** ast_say_date_with_format_he Say formmated date in Hebrew
3602 * \ref ast_say_date_with_format_en for the details of the options
3604 * Changes from the English version:
3606 * * don't replicate in here the logic of ast_say_number_full_he
3608 * * year is always 4-digit (because it's simpler)
3610 * * added c, x, and X. Mainly for my tests
3612 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3614 * TODO:
3615 * * A "ha" is missing in the standard date format, before the 'd'.
3616 * * The numbers of 3000--19000 are not handled well
3618 #define IL_DATE_STR "AdBY"
3619 #define IL_TIME_STR "IMp"
3620 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3621 int ast_say_date_with_format_he(struct ast_channel *chan, time_t time,
3622 const char *ints, const char *lang, const char *format,
3623 const char *timezone)
3625 /* TODO: This whole function is cut&paste from
3626 * ast_say_date_with_format_en . Is that considered acceptable?
3628 struct tm tm;
3629 int res=0, offset, sndoffset;
3630 char sndfile[256], nextmsg[256];
3632 if (!format)
3633 format = IL_DATE_STR_FULL;
3635 ast_localtime(&time,&tm,timezone);
3637 for (offset=0 ; format[offset] != '\0' ; offset++) {
3638 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3639 switch (format[offset]) {
3640 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3641 case '\'':
3642 /* Literal name of a sound file */
3643 sndoffset=0;
3644 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3645 sndfile[sndoffset] = format[offset];
3646 sndfile[sndoffset] = '\0';
3647 res = wait_file(chan,ints,sndfile,lang);
3648 break;
3649 case 'A':
3650 case 'a':
3651 /* Sunday - Saturday */
3652 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3653 res = wait_file(chan,ints,nextmsg,lang);
3654 break;
3655 case 'B':
3656 case 'b':
3657 case 'h':
3658 /* January - December */
3659 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3660 res = wait_file(chan,ints,nextmsg,lang);
3661 break;
3662 case 'd':
3663 case 'e': /* Day of the month */
3664 /* I'm not sure exactly what the parameters
3665 * audiofd and ctrlfd to
3666 * ast_say_number_full_he mean, but it seems
3667 * safe to pass -1 there.
3669 * At least in one of the pathes :-(
3671 res = ast_say_number_full_he(chan, tm.tm_mday,
3672 ints, lang, "m", -1, -1
3674 break;
3675 case 'Y': /* Year */
3676 res = ast_say_number_full_he(chan, tm.tm_year+1900,
3677 ints, lang, "f", -1, -1
3679 break;
3680 case 'I':
3681 case 'l': /* 12-Hour */
3683 int hour = tm.tm_hour;
3684 hour = hour%12;
3685 if (hour == 0) hour=12;
3687 res = ast_say_number_full_he(chan, hour,
3688 ints, lang, "f", -1, -1
3691 break;
3692 case 'H':
3693 case 'k': /* 24-Hour */
3694 /* With 'H' there is an 'oh' after a single-
3695 * digit hour */
3696 if ((format[offset] == 'H') &&
3697 (tm.tm_hour <10)&&(tm.tm_hour>0)
3698 ) { /* e.g. oh-eight */
3699 res = wait_file(chan,ints, "digits/oh",lang);
3702 res = ast_say_number_full_he(chan, tm.tm_hour,
3703 ints, lang, "f", -1, -1
3705 break;
3706 case 'M': /* Minute */
3707 res = ast_say_number_full_he(chan, tm.tm_min,
3708 ints, lang,"f", -1, -1
3710 break;
3711 case 'P':
3712 case 'p':
3713 /* AM/PM */
3714 if (tm.tm_hour > 11)
3715 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3716 else
3717 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3718 res = wait_file(chan,ints,nextmsg,lang);
3719 break;
3720 case 'Q':
3721 /* Shorthand for "Today", "Yesterday", or "date" */
3722 case 'q':
3723 /* Shorthand for "" (today), "Yesterday", A
3724 * (weekday), or "date" */
3725 /* XXX As emphasized elsewhere, this should the native way in your
3726 * language to say the date, with changes in what you say, depending
3727 * upon how recent the date is. XXX */
3729 struct timeval now;
3730 struct tm tmnow;
3731 time_t beg_today, tt;
3732 char todo = format[offset]; /* The letter to format*/
3734 gettimeofday(&now,NULL);
3735 tt = now.tv_sec;
3736 ast_localtime(&tt,&tmnow,timezone);
3737 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3738 /* In any case, it saves not having to do ast_mktime() */
3739 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3740 if (beg_today < time) {
3741 /* Today */
3742 if (todo == 'Q') {
3743 res = wait_file(chan,
3744 ints,
3745 "digits/today",
3746 lang);
3748 } else if (beg_today - 86400 < time) {
3749 /* Yesterday */
3750 res = wait_file(chan,ints, "digits/yesterday",lang);
3751 } else if ((todo != 'Q') &&
3752 (beg_today - 86400 * 6 < time))
3754 /* Within the last week */
3755 res = ast_say_date_with_format_he(chan,
3756 time, ints, lang,
3757 "A", timezone);
3758 } else {
3759 res = ast_say_date_with_format_he(chan,
3760 time, ints, lang,
3761 IL_DATE_STR, timezone);
3764 break;
3765 case 'R':
3766 res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
3767 break;
3768 case 'S': /* Seconds */
3769 res = ast_say_number_full_he(chan, tm.tm_sec,
3770 ints, lang, "f", -1, -1
3772 break;
3773 case 'T':
3774 res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
3775 break;
3776 /* c, x, and X seem useful for testing. Not sure
3777 * if thiey're good for the general public */
3778 case 'c':
3779 res = ast_say_date_with_format_he(chan, time,
3780 ints, lang, IL_DATE_STR_FULL, timezone);
3781 break;
3782 case 'x':
3783 res = ast_say_date_with_format_he(chan, time,
3784 ints, lang, IL_DATE_STR, timezone);
3785 break;
3786 case 'X': /* Currently not locale-dependent...*/
3787 res = ast_say_date_with_format_he(chan, time,
3788 ints, lang, IL_TIME_STR, timezone);
3789 break;
3790 case ' ':
3791 case ' ':
3792 /* Just ignore spaces and tabs */
3793 break;
3794 default:
3795 /* Unknown character */
3796 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3798 /* Jump out on DTMF */
3799 if (res) {
3800 break;
3803 return res;
3807 /* Spanish syntax */
3808 int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
3810 struct tm tm;
3811 int res=0, offset, sndoffset;
3812 char sndfile[256], nextmsg[256];
3814 if (format == NULL)
3815 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3817 ast_localtime(&time,&tm,timezone);
3819 for (offset=0 ; format[offset] != '\0' ; offset++) {
3820 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3821 switch (format[offset]) {
3822 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3823 case '\'':
3824 /* Literal name of a sound file */
3825 sndoffset=0;
3826 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3827 sndfile[sndoffset] = format[offset];
3828 sndfile[sndoffset] = '\0';
3829 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
3830 res = wait_file(chan,ints,nextmsg,lang);
3831 break;
3832 case 'A':
3833 case 'a':
3834 /* Sunday - Saturday */
3835 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3836 res = wait_file(chan,ints,nextmsg,lang);
3837 break;
3838 case 'B':
3839 case 'b':
3840 case 'h':
3841 /* January - December */
3842 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3843 res = wait_file(chan,ints,nextmsg,lang);
3844 break;
3845 case 'm':
3846 /* First - Twelfth */
3847 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
3848 res = wait_file(chan,ints,nextmsg,lang);
3849 break;
3850 case 'd':
3851 case 'e':
3852 /* First - Thirtyfirst */
3853 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
3854 break;
3855 case 'Y':
3856 /* Year */
3857 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3858 break;
3859 case 'I':
3860 case 'l':
3861 /* 12-Hour */
3862 if (tm.tm_hour == 0)
3863 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3864 else if (tm.tm_hour > 12)
3865 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3866 else
3867 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3868 res = wait_file(chan,ints,nextmsg,lang);
3869 break;
3870 case 'H':
3871 case 'k':
3872 /* 24-Hour */
3873 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
3874 break;
3875 case 'M':
3876 /* Minute */
3877 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3878 break;
3879 case 'P':
3880 case 'p':
3881 /* AM/PM */
3882 if (tm.tm_hour > 18)
3883 res = wait_file(chan, ints, "digits/p-m", lang);
3884 else if (tm.tm_hour > 12)
3885 res = wait_file(chan, ints, "digits/afternoon", lang);
3886 else if (tm.tm_hour)
3887 res = wait_file(chan, ints, "digits/a-m", lang);
3888 break;
3889 case 'Q':
3890 /* Shorthand for "Today", "Yesterday", or ABdY */
3891 /* XXX As emphasized elsewhere, this should the native way in your
3892 * language to say the date, with changes in what you say, depending
3893 * upon how recent the date is. XXX */
3895 struct timeval now;
3896 struct tm tmnow;
3897 time_t beg_today, tt;
3899 gettimeofday(&now,NULL);
3900 tt = now.tv_sec;
3901 ast_localtime(&tt,&tmnow,timezone);
3902 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3903 /* In any case, it saves not having to do ast_mktime() */
3904 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3905 if (beg_today < time) {
3906 /* Today */
3907 res = wait_file(chan,ints, "digits/today",lang);
3908 } else if (beg_today - 86400 < time) {
3909 /* Yesterday */
3910 res = wait_file(chan,ints, "digits/yesterday",lang);
3911 } else {
3912 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3915 break;
3916 case 'q':
3917 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3918 /* XXX As emphasized elsewhere, this should the native way in your
3919 * language to say the date, with changes in what you say, depending
3920 * upon how recent the date is. XXX */
3922 struct timeval now;
3923 struct tm tmnow;
3924 time_t beg_today, tt;
3926 gettimeofday(&now,NULL);
3927 tt = now.tv_sec;
3928 ast_localtime(&tt,&tmnow,timezone);
3929 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3930 /* In any case, it saves not having to do ast_mktime() */
3931 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3932 if (beg_today < time) {
3933 /* Today */
3934 res = wait_file(chan,ints, "digits/today",lang);
3935 } else if ((beg_today - 86400) < time) {
3936 /* Yesterday */
3937 res = wait_file(chan,ints, "digits/yesterday",lang);
3938 } else if (beg_today - 86400 * 6 < time) {
3939 /* Within the last week */
3940 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
3941 } else {
3942 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3945 break;
3946 case 'R':
3947 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
3948 break;
3949 case 'S':
3950 /* Seconds */
3951 if (tm.tm_sec == 0) {
3952 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3953 res = wait_file(chan,ints,nextmsg,lang);
3954 } else if (tm.tm_sec < 10) {
3955 res = wait_file(chan,ints, "digits/oh",lang);
3956 if (!res) {
3957 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3958 res = wait_file(chan,ints,nextmsg,lang);
3960 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
3961 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3962 res = wait_file(chan,ints,nextmsg,lang);
3963 } else {
3964 int ten, one;
3965 ten = (tm.tm_sec / 10) * 10;
3966 one = (tm.tm_sec % 10);
3967 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
3968 res = wait_file(chan,ints,nextmsg,lang);
3969 if (!res) {
3970 /* Fifty, not fifty-zero */
3971 if (one != 0) {
3972 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
3973 res = wait_file(chan,ints,nextmsg,lang);
3977 break;
3978 case 'T':
3979 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
3980 break;
3981 case ' ':
3982 case ' ':
3983 /* Just ignore spaces and tabs */
3984 break;
3985 default:
3986 /* Unknown character */
3987 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3989 /* Jump out on DTMF */
3990 if (res) {
3991 break;
3994 return res;
3997 /* French syntax
3998 oclock = heure
4000 int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4002 struct tm tm;
4003 int res=0, offset, sndoffset;
4004 char sndfile[256], nextmsg[256];
4006 if (format == NULL)
4007 format = "AdBY 'digits/at' IMp";
4009 ast_localtime(&time,&tm,timezone);
4011 for (offset=0 ; format[offset] != '\0' ; offset++) {
4012 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4013 switch (format[offset]) {
4014 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4015 case '\'':
4016 /* Literal name of a sound file */
4017 sndoffset=0;
4018 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4019 sndfile[sndoffset] = format[offset];
4020 sndfile[sndoffset] = '\0';
4021 res = wait_file(chan,ints,sndfile,lang);
4022 break;
4023 case 'A':
4024 case 'a':
4025 /* Sunday - Saturday */
4026 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4027 res = wait_file(chan,ints,nextmsg,lang);
4028 break;
4029 case 'B':
4030 case 'b':
4031 case 'h':
4032 /* January - December */
4033 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4034 res = wait_file(chan,ints,nextmsg,lang);
4035 break;
4036 case 'm':
4037 /* First - Twelfth */
4038 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4039 res = wait_file(chan,ints,nextmsg,lang);
4040 break;
4041 case 'd':
4042 case 'e':
4043 /* First */
4044 if (tm.tm_mday == 1) {
4045 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4046 res = wait_file(chan,ints,nextmsg,lang);
4047 } else {
4048 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4050 break;
4051 case 'Y':
4052 /* Year */
4053 if (tm.tm_year > 99) {
4054 res = wait_file(chan,ints, "digits/2",lang);
4055 if (!res) {
4056 res = wait_file(chan,ints, "digits/thousand",lang);
4058 if (tm.tm_year > 100) {
4059 if (!res) {
4060 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4063 } else {
4064 if (tm.tm_year < 1) {
4065 /* I'm not going to handle 1900 and prior */
4066 /* We'll just be silent on the year, instead of bombing out. */
4067 } else {
4068 res = wait_file(chan,ints, "digits/thousand",lang);
4069 if (!res) {
4070 wait_file(chan,ints, "digits/9",lang);
4071 wait_file(chan,ints, "digits/hundred",lang);
4072 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4076 break;
4077 case 'I':
4078 case 'l':
4079 /* 12-Hour */
4080 if (tm.tm_hour == 0)
4081 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4082 else if (tm.tm_hour > 12)
4083 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4084 else
4085 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4086 res = wait_file(chan,ints,nextmsg,lang);
4087 if (!res)
4088 res = wait_file(chan,ints, "digits/oclock",lang);
4089 break;
4090 case 'H':
4091 case 'k':
4092 /* 24-Hour */
4093 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4094 if (!res)
4095 res = wait_file(chan,ints, "digits/oclock",lang);
4096 break;
4097 case 'M':
4098 /* Minute */
4099 if (tm.tm_min == 0) {
4100 break;
4102 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4103 break;
4104 case 'P':
4105 case 'p':
4106 /* AM/PM */
4107 if (tm.tm_hour > 11)
4108 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4109 else
4110 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4111 res = wait_file(chan,ints,nextmsg,lang);
4112 break;
4113 case 'Q':
4114 /* Shorthand for "Today", "Yesterday", or AdBY */
4115 /* XXX As emphasized elsewhere, this should the native way in your
4116 * language to say the date, with changes in what you say, depending
4117 * upon how recent the date is. XXX */
4119 struct timeval now;
4120 struct tm tmnow;
4121 time_t beg_today, tt;
4123 gettimeofday(&now,NULL);
4124 tt = now.tv_sec;
4125 ast_localtime(&tt,&tmnow,timezone);
4126 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4127 /* In any case, it saves not having to do ast_mktime() */
4128 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4129 if (beg_today < time) {
4130 /* Today */
4131 res = wait_file(chan,ints, "digits/today",lang);
4132 } else if (beg_today - 86400 < time) {
4133 /* Yesterday */
4134 res = wait_file(chan,ints, "digits/yesterday",lang);
4135 } else {
4136 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4139 break;
4140 case 'q':
4141 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4142 /* XXX As emphasized elsewhere, this should the native way in your
4143 * language to say the date, with changes in what you say, depending
4144 * upon how recent the date is. XXX */
4146 struct timeval now;
4147 struct tm tmnow;
4148 time_t beg_today, tt;
4150 gettimeofday(&now,NULL);
4151 tt = now.tv_sec;
4152 ast_localtime(&tt,&tmnow,timezone);
4153 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4154 /* In any case, it saves not having to do ast_mktime() */
4155 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4156 if (beg_today < time) {
4157 /* Today */
4158 } else if ((beg_today - 86400) < time) {
4159 /* Yesterday */
4160 res = wait_file(chan,ints, "digits/yesterday",lang);
4161 } else if (beg_today - 86400 * 6 < time) {
4162 /* Within the last week */
4163 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4164 } else {
4165 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4168 break;
4169 case 'R':
4170 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4171 break;
4172 case 'S':
4173 /* Seconds */
4174 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4175 if (!res) {
4176 res = wait_file(chan,ints, "digits/second",lang);
4178 break;
4179 case 'T':
4180 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4181 break;
4182 case ' ':
4183 case ' ':
4184 /* Just ignore spaces and tabs */
4185 break;
4186 default:
4187 /* Unknown character */
4188 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4190 /* Jump out on DTMF */
4191 if (res) {
4192 break;
4195 return res;
4198 int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4200 struct tm tm;
4201 int res=0, offset, sndoffset;
4202 char sndfile[256], nextmsg[256];
4204 if (format == NULL)
4205 format = "AdB 'digits/at' IMp";
4207 ast_localtime(&time,&tm,timezone);
4209 for (offset=0 ; format[offset] != '\0' ; offset++) {
4210 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4211 switch (format[offset]) {
4212 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4213 case '\'':
4214 /* Literal name of a sound file */
4215 sndoffset=0;
4216 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4217 sndfile[sndoffset] = format[offset];
4218 sndfile[sndoffset] = '\0';
4219 res = wait_file(chan,ints,sndfile,lang);
4220 break;
4221 case 'A':
4222 case 'a':
4223 /* Sunday - Saturday */
4224 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4225 res = wait_file(chan,ints,nextmsg,lang);
4226 break;
4227 case 'B':
4228 case 'b':
4229 case 'h':
4230 /* January - December */
4231 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4232 res = wait_file(chan,ints,nextmsg,lang);
4233 break;
4234 case 'm':
4235 /* First - Twelfth */
4236 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4237 res = wait_file(chan,ints,nextmsg,lang);
4238 break;
4239 case 'd':
4240 case 'e':
4241 /* First day of the month is spelled as ordinal */
4242 if (tm.tm_mday == 1) {
4243 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4244 res = wait_file(chan,ints,nextmsg,lang);
4245 } else {
4246 if (!res) {
4247 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4250 break;
4251 case 'Y':
4252 /* Year */
4253 if (tm.tm_year > 99) {
4254 res = wait_file(chan,ints, "digits/ore-2000",lang);
4255 if (tm.tm_year > 100) {
4256 if (!res) {
4257 /* This works until the end of 2021 */
4258 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4259 res = wait_file(chan,ints,nextmsg,lang);
4262 } else {
4263 if (tm.tm_year < 1) {
4264 /* I'm not going to handle 1900 and prior */
4265 /* We'll just be silent on the year, instead of bombing out. */
4266 } else {
4267 res = wait_file(chan,ints, "digits/ore-1900",lang);
4268 if ((!res) && (tm.tm_year != 0)) {
4269 if (tm.tm_year <= 21) {
4270 /* 1910 - 1921 */
4271 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4272 res = wait_file(chan,ints,nextmsg,lang);
4273 } else {
4274 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4275 int ten, one;
4276 ten = tm.tm_year / 10;
4277 one = tm.tm_year % 10;
4278 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4279 res = wait_file(chan,ints,nextmsg,lang);
4280 if (!res) {
4281 if (one != 0) {
4282 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4283 res = wait_file(chan,ints,nextmsg,lang);
4290 break;
4291 case 'I':
4292 case 'l':
4293 /* 12-Hour */
4294 if (tm.tm_hour == 0)
4295 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4296 else if (tm.tm_hour > 12)
4297 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4298 else
4299 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4300 res = wait_file(chan,ints,nextmsg,lang);
4301 break;
4302 case 'H':
4303 case 'k':
4304 /* 24-Hour */
4305 if (tm.tm_hour == 0) {
4306 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4307 } else if (tm.tm_hour == 1) {
4308 res = wait_file(chan,ints, "digits/ore-una",lang);
4309 } else {
4310 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4312 break;
4313 case 'M':
4314 /* Minute */
4315 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4316 break;
4317 case 'P':
4318 case 'p':
4319 /* AM/PM */
4320 if (tm.tm_hour > 11)
4321 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4322 else
4323 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4324 res = wait_file(chan,ints,nextmsg,lang);
4325 break;
4326 case 'Q':
4327 /* Shorthand for "Today", "Yesterday", or ABdY */
4328 /* XXX As emphasized elsewhere, this should the native way in your
4329 * language to say the date, with changes in what you say, depending
4330 * upon how recent the date is. XXX */
4332 struct timeval now;
4333 struct tm tmnow;
4334 time_t beg_today, tt;
4336 gettimeofday(&now,NULL);
4337 tt = now.tv_sec;
4338 ast_localtime(&tt,&tmnow,timezone);
4339 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4340 /* In any case, it saves not having to do ast_mktime() */
4341 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4342 if (beg_today < time) {
4343 /* Today */
4344 res = wait_file(chan,ints, "digits/today",lang);
4345 } else if (beg_today - 86400 < time) {
4346 /* Yesterday */
4347 res = wait_file(chan,ints, "digits/yesterday",lang);
4348 } else {
4349 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4352 break;
4353 case 'q':
4354 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4356 struct timeval now;
4357 struct tm tmnow;
4358 time_t beg_today, tt;
4360 gettimeofday(&now,NULL);
4361 tt = now.tv_sec;
4362 ast_localtime(&tt,&tmnow,timezone);
4363 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4364 /* In any case, it saves not having to do ast_mktime() */
4365 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4366 if (beg_today < time) {
4367 /* Today */
4368 } else if ((beg_today - 86400) < time) {
4369 /* Yesterday */
4370 res = wait_file(chan,ints, "digits/yesterday",lang);
4371 } else if (beg_today - 86400 * 6 < time) {
4372 /* Within the last week */
4373 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4374 } else {
4375 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4378 break;
4379 case 'R':
4380 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4381 break;
4382 case 'S':
4383 /* Seconds */
4384 if (tm.tm_sec == 0) {
4385 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4386 res = wait_file(chan,ints,nextmsg,lang);
4387 } else if (tm.tm_sec < 10) {
4388 res = wait_file(chan,ints, "digits/oh",lang);
4389 if (!res) {
4390 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4391 res = wait_file(chan,ints,nextmsg,lang);
4393 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4394 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4395 res = wait_file(chan,ints,nextmsg,lang);
4396 } else {
4397 int ten, one;
4398 ten = (tm.tm_sec / 10) * 10;
4399 one = (tm.tm_sec % 10);
4400 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4401 res = wait_file(chan,ints,nextmsg,lang);
4402 if (!res) {
4403 /* Fifty, not fifty-zero */
4404 if (one != 0) {
4405 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4406 res = wait_file(chan,ints,nextmsg,lang);
4410 break;
4411 case 'T':
4412 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4413 break;
4414 case ' ':
4415 case ' ':
4416 /* Just ignore spaces and tabs */
4417 break;
4418 default:
4419 /* Unknown character */
4420 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4422 /* Jump out on DTMF */
4423 if (res) {
4424 break;
4427 return res;
4430 /* Dutch syntax */
4431 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4433 struct tm tm;
4434 int res=0, offset, sndoffset;
4435 char sndfile[256], nextmsg[256];
4437 if (format == NULL)
4438 format = "ABdY 'digits/at' IMp";
4440 ast_localtime(&time,&tm,timezone);
4442 for (offset=0 ; format[offset] != '\0' ; offset++) {
4443 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4444 switch (format[offset]) {
4445 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4446 case '\'':
4447 /* Literal name of a sound file */
4448 sndoffset=0;
4449 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4450 sndfile[sndoffset] = format[offset];
4451 sndfile[sndoffset] = '\0';
4452 res = wait_file(chan,ints,sndfile,lang);
4453 break;
4454 case 'A':
4455 case 'a':
4456 /* Sunday - Saturday */
4457 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4458 res = wait_file(chan,ints,nextmsg,lang);
4459 break;
4460 case 'B':
4461 case 'b':
4462 case 'h':
4463 /* January - December */
4464 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4465 res = wait_file(chan,ints,nextmsg,lang);
4466 break;
4467 case 'm':
4468 /* First - Twelfth */
4469 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4470 res = wait_file(chan,ints,nextmsg,lang);
4471 break;
4472 case 'd':
4473 case 'e':
4474 /* First - Thirtyfirst */
4475 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4476 break;
4477 case 'Y':
4478 /* Year */
4479 if (tm.tm_year > 99) {
4480 res = wait_file(chan,ints, "digits/2",lang);
4481 if (!res) {
4482 res = wait_file(chan,ints, "digits/thousand",lang);
4484 if (tm.tm_year > 100) {
4485 if (!res) {
4486 /* This works until the end of 2020 */
4487 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4488 res = wait_file(chan,ints,nextmsg,lang);
4491 } else {
4492 if (tm.tm_year < 1) {
4493 /* I'm not going to handle 1900 and prior */
4494 /* We'll just be silent on the year, instead of bombing out. */
4495 } else {
4496 res = wait_file(chan,ints, "digits/19",lang);
4497 if (!res) {
4498 if (tm.tm_year <= 9) {
4499 /* 1901 - 1909 */
4500 res = wait_file(chan,ints, "digits/oh",lang);
4501 if (!res) {
4502 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4503 res = wait_file(chan,ints,nextmsg,lang);
4505 } else if (tm.tm_year <= 20) {
4506 /* 1910 - 1920 */
4507 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4508 res = wait_file(chan,ints,nextmsg,lang);
4509 } else {
4510 /* 1921 - 1999 */
4511 int ten, one;
4512 ten = tm.tm_year / 10;
4513 one = tm.tm_year % 10;
4514 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4515 res = wait_file(chan,ints,nextmsg,lang);
4516 if (!res) {
4517 if (one != 0) {
4518 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4519 res = wait_file(chan,ints,nextmsg,lang);
4526 break;
4527 case 'I':
4528 case 'l':
4529 /* 12-Hour */
4530 if (tm.tm_hour == 0)
4531 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4532 else if (tm.tm_hour > 12)
4533 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4534 else
4535 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4536 res = wait_file(chan,ints,nextmsg,lang);
4537 break;
4538 case 'H':
4539 case 'k':
4540 /* 24-Hour */
4541 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4542 if (!res) {
4543 res = wait_file(chan,ints, "digits/nl-uur",lang);
4545 break;
4546 case 'M':
4547 /* Minute */
4548 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4549 break;
4550 case 'P':
4551 case 'p':
4552 /* AM/PM */
4553 if (tm.tm_hour > 11)
4554 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4555 else
4556 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4557 res = wait_file(chan,ints,nextmsg,lang);
4558 break;
4559 case 'Q':
4560 /* Shorthand for "Today", "Yesterday", or ABdY */
4561 /* XXX As emphasized elsewhere, this should the native way in your
4562 * language to say the date, with changes in what you say, depending
4563 * upon how recent the date is. XXX */
4565 struct timeval now;
4566 struct tm tmnow;
4567 time_t beg_today, tt;
4569 gettimeofday(&now,NULL);
4570 tt = now.tv_sec;
4571 ast_localtime(&tt,&tmnow,timezone);
4572 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4573 /* In any case, it saves not having to do ast_mktime() */
4574 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4575 if (beg_today < time) {
4576 /* Today */
4577 res = wait_file(chan,ints, "digits/today",lang);
4578 } else if (beg_today - 86400 < time) {
4579 /* Yesterday */
4580 res = wait_file(chan,ints, "digits/yesterday",lang);
4581 } else {
4582 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4585 break;
4586 case 'q':
4587 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4589 struct timeval now;
4590 struct tm tmnow;
4591 time_t beg_today, tt;
4593 gettimeofday(&now,NULL);
4594 tt = now.tv_sec;
4595 ast_localtime(&tt,&tmnow,timezone);
4596 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4597 /* In any case, it saves not having to do ast_mktime() */
4598 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4599 if (beg_today < time) {
4600 /* Today */
4601 } else if ((beg_today - 86400) < time) {
4602 /* Yesterday */
4603 res = wait_file(chan,ints, "digits/yesterday",lang);
4604 } else if (beg_today - 86400 * 6 < time) {
4605 /* Within the last week */
4606 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4607 } else {
4608 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4611 break;
4612 case 'R':
4613 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4614 break;
4615 case 'S':
4616 /* Seconds */
4617 if (tm.tm_sec == 0) {
4618 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4619 res = wait_file(chan,ints,nextmsg,lang);
4620 } else if (tm.tm_sec < 10) {
4621 res = wait_file(chan,ints, "digits/oh",lang);
4622 if (!res) {
4623 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4624 res = wait_file(chan,ints,nextmsg,lang);
4626 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4627 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4628 res = wait_file(chan,ints,nextmsg,lang);
4629 } else {
4630 int ten, one;
4631 ten = (tm.tm_sec / 10) * 10;
4632 one = (tm.tm_sec % 10);
4633 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4634 res = wait_file(chan,ints,nextmsg,lang);
4635 if (!res) {
4636 /* Fifty, not fifty-zero */
4637 if (one != 0) {
4638 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4639 res = wait_file(chan,ints,nextmsg,lang);
4643 break;
4644 case 'T':
4645 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4646 break;
4647 case ' ':
4648 case ' ':
4649 /* Just ignore spaces and tabs */
4650 break;
4651 default:
4652 /* Unknown character */
4653 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4655 /* Jump out on DTMF */
4656 if (res) {
4657 break;
4660 return res;
4663 /* Polish syntax */
4664 int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *timezone)
4666 struct tm tm;
4667 int res=0, offset, sndoffset;
4668 char sndfile[256], nextmsg[256];
4670 ast_localtime(&thetime, &tm, timezone);
4672 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4673 int remainder;
4674 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4675 switch (format[offset]) {
4676 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4677 case '\'':
4678 /* Literal name of a sound file */
4679 sndoffset = 0;
4680 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4681 sndfile[sndoffset] = format[offset];
4682 sndfile[sndoffset] = '\0';
4683 res = wait_file(chan, ints, sndfile, lang);
4684 break;
4685 case 'A':
4686 case 'a':
4687 /* Sunday - Saturday */
4688 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4689 res = wait_file(chan, ints, nextmsg, lang);
4690 break;
4691 case 'B':
4692 case 'b':
4693 case 'h':
4694 /* January - December */
4695 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4696 res = wait_file(chan, ints, nextmsg, lang);
4697 break;
4698 case 'm':
4699 /* Month enumerated */
4700 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4701 break;
4702 case 'd':
4703 case 'e':
4704 /* First - Thirtyfirst */
4705 remainder = tm.tm_mday;
4706 if (tm.tm_mday > 30) {
4707 res = wait_file(chan, ints, "digits/h-30", lang);
4708 remainder -= 30;
4710 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
4711 res = wait_file(chan, ints, "digits/h-20", lang);
4712 remainder -= 20;
4714 if (!res) {
4715 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4716 res = wait_file(chan, ints, nextmsg, lang);
4718 break;
4719 case 'Y':
4720 /* Year */
4721 if (tm.tm_year > 100) {
4722 res = wait_file(chan, ints, "digits/2", lang);
4723 if (!res)
4724 res = wait_file(chan, ints, "digits/1000.2",lang);
4725 if (tm.tm_year > 100) {
4726 if (!res)
4727 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4729 } else if (tm.tm_year == 100) {
4730 res = wait_file(chan, ints, "digits/h-2000", lang);
4731 } else {
4732 if (tm.tm_year < 1) {
4733 /* I'm not going to handle 1900 and prior */
4734 /* We'll just be silent on the year, instead of bombing out. */
4735 break;
4736 } else {
4737 res = wait_file(chan, ints, "digits/1000", lang);
4738 if (!res) {
4739 wait_file(chan, ints, "digits/900", lang);
4740 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4744 if (!res)
4745 wait_file(chan, ints, "digits/year", lang);
4746 break;
4747 case 'I':
4748 case 'l':
4749 /* 12-Hour */
4750 if (tm.tm_hour == 0)
4751 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4752 else if (tm.tm_hour > 12)
4753 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4754 else
4755 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4757 res = wait_file(chan, ints, nextmsg, lang);
4758 break;
4759 case 'H':
4760 case 'k':
4761 /* 24-Hour */
4762 if (tm.tm_hour != 0) {
4763 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4764 res = wait_file(chan, ints, nextmsg, lang);
4765 } else
4766 res = wait_file(chan, ints, "digits/t-24", lang);
4767 break;
4768 case 'M':
4769 case 'N':
4770 /* Minute */
4771 if (tm.tm_min == 0) {
4772 if (format[offset] == 'M') {
4773 res = wait_file(chan, ints, "digits/oclock", lang);
4774 } else {
4775 res = wait_file(chan, ints, "digits/100", lang);
4777 } else
4778 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4779 break;
4780 case 'P':
4781 case 'p':
4782 /* AM/PM */
4783 if (tm.tm_hour > 11)
4784 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4785 else
4786 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4787 res = wait_file(chan, ints, nextmsg, lang);
4788 break;
4789 case 'Q':
4790 /* Shorthand for "Today", "Yesterday", or AdBY */
4792 time_t tv_sec = time(NULL);
4793 struct tm tmnow;
4794 time_t beg_today;
4796 ast_localtime(&tv_sec,&tmnow, timezone);
4797 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4798 /* In any case, it saves not having to do ast_mktime() */
4799 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4800 if (beg_today < thetime) {
4801 /* Today */
4802 res = wait_file(chan, ints, "digits/today", lang);
4803 } else if (beg_today - 86400 < thetime) {
4804 /* Yesterday */
4805 res = wait_file(chan, ints, "digits/yesterday", lang);
4806 } else {
4807 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4810 break;
4811 case 'q':
4812 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4814 time_t tv_sec = time(NULL);
4815 struct tm tmnow;
4816 time_t beg_today;
4818 ast_localtime(&tv_sec, &tmnow, timezone);
4819 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4820 /* In any case, it saves not having to do ast_mktime() */
4821 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4822 if (beg_today < thetime) {
4823 /* Today */
4824 } else if ((beg_today - 86400) < thetime) {
4825 /* Yesterday */
4826 res = wait_file(chan, ints, "digits/yesterday", lang);
4827 } else if (beg_today - 86400 * 6 < thetime) {
4828 /* Within the last week */
4829 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
4830 } else {
4831 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4834 break;
4835 case 'R':
4836 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
4837 break;
4838 case 'S':
4839 /* Seconds */
4840 res = wait_file(chan, ints, "digits/and", lang);
4841 if (!res) {
4842 if (tm.tm_sec == 1) {
4843 res = wait_file(chan, ints, "digits/1z", lang);
4844 if (!res)
4845 res = wait_file(chan, ints, "digits/second-a", lang);
4846 } else {
4847 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4848 if (!res) {
4849 int ten, one;
4850 ten = tm.tm_sec / 10;
4851 one = tm.tm_sec % 10;
4853 if (one > 1 && one < 5 && ten != 1)
4854 res = wait_file(chan,ints, "digits/seconds",lang);
4855 else
4856 res = wait_file(chan,ints, "digits/second",lang);
4860 break;
4861 case 'T':
4862 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
4863 break;
4864 case ' ':
4865 case ' ':
4866 /* Just ignore spaces and tabs */
4867 break;
4868 default:
4869 /* Unknown character */
4870 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4872 /* Jump out on DTMF */
4873 if (res)
4874 break;
4876 return res;
4879 /* Portuguese syntax */
4880 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
4882 struct tm tm;
4883 int res=0, offset, sndoffset;
4884 char sndfile[256], nextmsg[256];
4886 if (format == NULL)
4887 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4889 ast_localtime(&time,&tm,timezone);
4891 for (offset=0 ; format[offset] != '\0' ; offset++) {
4892 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4893 switch (format[offset]) {
4894 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4895 case '\'':
4896 /* Literal name of a sound file */
4897 sndoffset=0;
4898 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4899 sndfile[sndoffset] = format[offset];
4900 sndfile[sndoffset] = '\0';
4901 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4902 res = wait_file(chan,ints,nextmsg,lang);
4903 break;
4904 case 'A':
4905 case 'a':
4906 /* Sunday - Saturday */
4907 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4908 res = wait_file(chan,ints,nextmsg,lang);
4909 break;
4910 case 'B':
4911 case 'b':
4912 case 'h':
4913 /* January - December */
4914 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4915 res = wait_file(chan,ints,nextmsg,lang);
4916 break;
4917 case 'm':
4918 /* First - Twelfth */
4919 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4920 res = wait_file(chan,ints,nextmsg,lang);
4921 break;
4922 case 'd':
4923 case 'e':
4924 /* First - Thirtyfirst */
4925 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4926 break;
4927 case 'Y':
4928 /* Year */
4929 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4930 break;
4931 case 'I':
4932 case 'l':
4933 /* 12-Hour */
4934 if (tm.tm_hour == 0) {
4935 if (format[offset] == 'I')
4936 res = wait_file(chan, ints, "digits/pt-ah", lang);
4937 if (!res)
4938 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4940 else if (tm.tm_hour == 12) {
4941 if (format[offset] == 'I')
4942 res = wait_file(chan, ints, "digits/pt-ao", lang);
4943 if (!res)
4944 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4946 else {
4947 if (format[offset] == 'I') {
4948 res = wait_file(chan, ints, "digits/pt-ah", lang);
4949 if ((tm.tm_hour % 12) != 1)
4950 if (!res)
4951 res = wait_file(chan, ints, "digits/pt-sss", lang);
4953 if (!res)
4954 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
4956 break;
4957 case 'H':
4958 case 'k':
4959 /* 24-Hour */
4960 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
4961 if (!res) {
4962 if (tm.tm_hour != 0) {
4963 int remainder = tm.tm_hour;
4964 if (tm.tm_hour > 20) {
4965 res = wait_file(chan,ints, "digits/20",lang);
4966 remainder -= 20;
4968 if (!res) {
4969 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
4970 res = wait_file(chan,ints,nextmsg,lang);
4974 break;
4975 case 'M':
4976 /* Minute */
4977 if (tm.tm_min == 0) {
4978 res = wait_file(chan, ints, "digits/pt-hora", lang);
4979 if (tm.tm_hour != 1)
4980 if (!res)
4981 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
4982 res = wait_file(chan,ints,"digits/pt-e",lang);
4983 if (!res)
4984 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4986 break;
4987 case 'P':
4988 case 'p':
4989 /* AM/PM */
4990 if (tm.tm_hour > 12)
4991 res = wait_file(chan, ints, "digits/p-m", lang);
4992 else if (tm.tm_hour && tm.tm_hour < 12)
4993 res = wait_file(chan, ints, "digits/a-m", lang);
4994 break;
4995 case 'Q':
4996 /* Shorthand for "Today", "Yesterday", or ABdY */
4997 /* XXX As emphasized elsewhere, this should the native way in your
4998 * language to say the date, with changes in what you say, depending
4999 * upon how recent the date is. XXX */
5001 struct timeval now;
5002 struct tm tmnow;
5003 time_t beg_today, tt;
5005 gettimeofday(&now,NULL);
5006 tt = now.tv_sec;
5007 ast_localtime(&tt,&tmnow,timezone);
5008 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5009 /* In any case, it saves not having to do ast_mktime() */
5010 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5011 if (beg_today < time) {
5012 /* Today */
5013 res = wait_file(chan,ints, "digits/today",lang);
5014 } else if (beg_today - 86400 < time) {
5015 /* Yesterday */
5016 res = wait_file(chan,ints, "digits/yesterday",lang);
5017 } else {
5018 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5021 break;
5022 case 'q':
5023 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5024 /* XXX As emphasized elsewhere, this should the native way in your
5025 * language to say the date, with changes in what you say, depending
5026 * upon how recent the date is. XXX */
5028 struct timeval now;
5029 struct tm tmnow;
5030 time_t beg_today, tt;
5032 gettimeofday(&now,NULL);
5033 tt = now.tv_sec;
5034 ast_localtime(&tt,&tmnow,timezone);
5035 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5036 /* In any case, it saves not having to do ast_mktime() */
5037 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5038 if (beg_today < time) {
5039 /* Today */
5040 } else if ((beg_today - 86400) < time) {
5041 /* Yesterday */
5042 res = wait_file(chan,ints, "digits/yesterday",lang);
5043 } else if (beg_today - 86400 * 6 < time) {
5044 /* Within the last week */
5045 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5046 } else {
5047 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5050 break;
5051 case 'R':
5052 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5053 break;
5054 case 'S':
5055 /* Seconds */
5056 if (tm.tm_sec == 0) {
5057 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5058 res = wait_file(chan,ints,nextmsg,lang);
5059 } else if (tm.tm_sec < 10) {
5060 res = wait_file(chan,ints, "digits/oh",lang);
5061 if (!res) {
5062 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5063 res = wait_file(chan,ints,nextmsg,lang);
5065 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5066 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5067 res = wait_file(chan,ints,nextmsg,lang);
5068 } else {
5069 int ten, one;
5070 ten = (tm.tm_sec / 10) * 10;
5071 one = (tm.tm_sec % 10);
5072 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5073 res = wait_file(chan,ints,nextmsg,lang);
5074 if (!res) {
5075 /* Fifty, not fifty-zero */
5076 if (one != 0) {
5077 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5078 res = wait_file(chan,ints,nextmsg,lang);
5082 break;
5083 case 'T':
5084 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5085 break;
5086 case ' ':
5087 case ' ':
5088 /* Just ignore spaces and tabs */
5089 break;
5090 default:
5091 /* Unknown character */
5092 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5094 /* Jump out on DTMF */
5095 if (res) {
5096 break;
5099 return res;
5102 /* Taiwanese / Chinese syntax */
5103 int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
5105 struct tm tm;
5106 int res=0, offset, sndoffset;
5107 char sndfile[256], nextmsg[256];
5109 if (format == NULL)
5110 format = "YBdA 'digits/at' HM";
5112 ast_localtime(&time,&tm,timezone);
5114 for (offset=0 ; format[offset] != '\0' ; offset++) {
5115 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5116 switch (format[offset]) {
5117 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5118 case '\'':
5119 /* Literal name of a sound file */
5120 sndoffset=0;
5121 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5122 sndfile[sndoffset] = format[offset];
5123 sndfile[sndoffset] = '\0';
5124 res = wait_file(chan,ints,sndfile,lang);
5125 break;
5126 case 'A':
5127 case 'a':
5128 /* Sunday - Saturday */
5129 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5130 res = wait_file(chan,ints,nextmsg,lang);
5131 break;
5132 case 'B':
5133 case 'b':
5134 case 'h':
5135 /* January - December */
5136 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5137 res = wait_file(chan,ints,nextmsg,lang);
5138 break;
5139 case 'm':
5140 /* First - Twelfth */
5141 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5142 res = wait_file(chan,ints,nextmsg,lang);
5143 break;
5144 case 'd':
5145 case 'e':
5146 /* First - Thirtyfirst */
5147 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5148 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5149 res = wait_file(chan,ints,nextmsg,lang);
5150 } else {
5151 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%dh", tm.tm_mday - (tm.tm_mday % 10));
5152 res = wait_file(chan,ints,nextmsg,lang);
5153 if(!res) {
5154 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday % 10);
5155 res = wait_file(chan,ints,nextmsg,lang);
5158 break;
5159 case 'Y':
5160 /* Year */
5161 if (tm.tm_year > 99) {
5162 res = wait_file(chan,ints, "digits/2",lang);
5163 if (!res) {
5164 res = wait_file(chan,ints, "digits/thousand",lang);
5166 if (tm.tm_year > 100) {
5167 if (!res) {
5168 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5169 res = wait_file(chan,ints,nextmsg,lang);
5170 if (!res) {
5171 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5172 res = wait_file(chan,ints,nextmsg,lang);
5176 if (!res) {
5177 res = wait_file(chan,ints, "digits/year",lang);
5179 } else {
5180 if (tm.tm_year < 1) {
5181 /* I'm not going to handle 1900 and prior */
5182 /* We'll just be silent on the year, instead of bombing out. */
5183 } else {
5184 res = wait_file(chan,ints, "digits/1",lang);
5185 if (!res) {
5186 res = wait_file(chan,ints, "digits/9",lang);
5188 if (!res) {
5189 if (tm.tm_year <= 9) {
5190 /* 1901 - 1909 */
5191 res = wait_file(chan,ints, "digits/0",lang);
5192 if (!res) {
5193 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5194 res = wait_file(chan,ints,nextmsg,lang);
5196 } else {
5197 /* 1910 - 1999 */
5198 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5199 res = wait_file(chan,ints,nextmsg,lang);
5200 if (!res) {
5201 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5202 res = wait_file(chan,ints,nextmsg,lang);
5207 if (!res) {
5208 res = wait_file(chan,ints, "digits/year",lang);
5211 break;
5212 case 'I':
5213 case 'l':
5214 /* 12-Hour */
5215 if (tm.tm_hour == 0)
5216 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5217 else if (tm.tm_hour > 12)
5218 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5219 else
5220 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5221 res = wait_file(chan,ints,nextmsg,lang);
5222 if (!res) {
5223 res = wait_file(chan,ints, "digits/oclock",lang);
5225 break;
5226 case 'H':
5227 case 'k':
5228 /* 24-Hour */
5229 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5230 if (tm.tm_hour < 10) {
5231 res = wait_file(chan, ints, "digits/0", lang);
5233 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5234 res = wait_file(chan,ints,nextmsg,lang);
5235 } else {
5236 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5237 res = wait_file(chan,ints,nextmsg,lang);
5238 if (!res) {
5239 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5240 res = wait_file(chan,ints,nextmsg,lang);
5243 if (!res) {
5244 res = wait_file(chan,ints, "digits/oclock",lang);
5246 break;
5247 case 'M':
5248 /* Minute */
5249 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5250 if (tm.tm_min < 10) {
5251 res = wait_file(chan, ints, "digits/0", lang);
5253 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5254 res = wait_file(chan,ints,nextmsg,lang);
5255 } else {
5256 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5257 res = wait_file(chan,ints,nextmsg,lang);
5258 if (!res) {
5259 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5260 res = wait_file(chan,ints,nextmsg,lang);
5263 if (!res) {
5264 res = wait_file(chan,ints, "digits/minute",lang);
5266 break;
5267 case 'P':
5268 case 'p':
5269 /* AM/PM */
5270 if (tm.tm_hour > 11)
5271 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5272 else
5273 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5274 res = wait_file(chan,ints,nextmsg,lang);
5275 break;
5276 case 'Q':
5277 /* Shorthand for "Today", "Yesterday", or ABdY */
5278 /* XXX As emphasized elsewhere, this should the native way in your
5279 * language to say the date, with changes in what you say, depending
5280 * upon how recent the date is. XXX */
5282 struct timeval now;
5283 struct tm tmnow;
5284 time_t beg_today, tt;
5286 gettimeofday(&now,NULL);
5287 tt = now.tv_sec;
5288 ast_localtime(&tt,&tmnow,timezone);
5289 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5290 /* In any case, it saves not having to do ast_mktime() */
5291 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5292 if (beg_today < time) {
5293 /* Today */
5294 res = wait_file(chan,ints, "digits/today",lang);
5295 } else if (beg_today - 86400 < time) {
5296 /* Yesterday */
5297 res = wait_file(chan,ints, "digits/yesterday",lang);
5298 } else {
5299 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5302 break;
5303 case 'q':
5304 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5305 /* XXX As emphasized elsewhere, this should the native way in your
5306 * language to say the date, with changes in what you say, depending
5307 * upon how recent the date is. XXX */
5309 struct timeval now;
5310 struct tm tmnow;
5311 time_t beg_today, tt;
5313 gettimeofday(&now,NULL);
5314 tt = now.tv_sec;
5315 ast_localtime(&tt,&tmnow,timezone);
5316 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5317 /* In any case, it saves not having to do ast_mktime() */
5318 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5319 if (beg_today < time) {
5320 /* Today */
5321 } else if ((beg_today - 86400) < time) {
5322 /* Yesterday */
5323 res = wait_file(chan,ints, "digits/yesterday",lang);
5324 } else if (beg_today - 86400 * 6 < time) {
5325 /* Within the last week */
5326 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5327 } else {
5328 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5331 break;
5332 case 'R':
5333 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HM", timezone);
5334 break;
5335 case 'S':
5336 /* Seconds */
5337 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5338 if (tm.tm_sec < 10) {
5339 res = wait_file(chan, ints, "digits/0", lang);
5341 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5342 res = wait_file(chan,ints,nextmsg,lang);
5343 } else {
5344 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5345 res = wait_file(chan,ints,nextmsg,lang);
5346 if (!res) {
5347 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5348 res = wait_file(chan,ints,nextmsg,lang);
5351 if (!res) {
5352 res = wait_file(chan,ints, "digits/second",lang);
5354 break;
5355 case 'T':
5356 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5357 break;
5358 case ' ':
5359 case ' ':
5360 /* Just ignore spaces and tabs */
5361 break;
5362 default:
5363 /* Unknown character */
5364 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5366 /* Jump out on DTMF */
5367 if (res) {
5368 break;
5371 return res;
5374 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5376 if (!strcasecmp(lang, "en") ) { /* English syntax */
5377 return(ast_say_time_en(chan, t, ints, lang));
5378 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5379 return(ast_say_time_de(chan, t, ints, lang));
5380 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5381 return(ast_say_time_fr(chan, t, ints, lang));
5382 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5383 return(ast_say_time_nl(chan, t, ints, lang));
5384 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5385 return(ast_say_time_pt(chan, t, ints, lang));
5386 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5387 return(ast_say_time_tw(chan, t, ints, lang));
5388 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5389 return(ast_say_time_gr(chan, t, ints, lang));
5392 /* Default to English */
5393 return(ast_say_time_en(chan, t, ints, lang));
5396 /* English syntax */
5397 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5399 struct tm tm;
5400 int res = 0;
5401 int hour, pm=0;
5402 localtime_r(&t,&tm);
5403 hour = tm.tm_hour;
5404 if (!hour)
5405 hour = 12;
5406 else if (hour == 12)
5407 pm = 1;
5408 else if (hour > 12) {
5409 hour -= 12;
5410 pm = 1;
5412 if (!res)
5413 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5415 if (tm.tm_min > 9) {
5416 if (!res)
5417 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5418 } else if (tm.tm_min) {
5419 if (!res)
5420 res = ast_streamfile(chan, "digits/oh", lang);
5421 if (!res)
5422 res = ast_waitstream(chan, ints);
5423 if (!res)
5424 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5425 } else {
5426 if (!res)
5427 res = ast_streamfile(chan, "digits/oclock", lang);
5428 if (!res)
5429 res = ast_waitstream(chan, ints);
5431 if (pm) {
5432 if (!res)
5433 res = ast_streamfile(chan, "digits/p-m", lang);
5434 } else {
5435 if (!res)
5436 res = ast_streamfile(chan, "digits/a-m", lang);
5438 if (!res)
5439 res = ast_waitstream(chan, ints);
5440 return res;
5443 /* German syntax */
5444 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5446 struct tm tm;
5447 int res = 0;
5448 localtime_r(&t,&tm);
5449 if (!res)
5450 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5451 if (!res)
5452 res = ast_streamfile(chan, "digits/oclock", lang);
5453 if (!res)
5454 res = ast_waitstream(chan, ints);
5455 if (!res)
5456 if (tm.tm_min > 0)
5457 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5458 return res;
5461 /* French syntax */
5462 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5464 struct tm tm;
5465 int res = 0;
5466 localtime_r(&t,&tm);
5468 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5469 if (!res)
5470 res = ast_streamfile(chan, "digits/oclock", lang);
5471 if (tm.tm_min) {
5472 if (!res)
5473 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5475 return res;
5478 /* Dutch syntax */
5479 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5481 struct tm tm;
5482 int res = 0;
5483 localtime_r(&t,&tm);
5484 if (!res)
5485 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5486 if (!res)
5487 res = ast_streamfile(chan, "digits/nl-uur", lang);
5488 if (!res)
5489 res = ast_waitstream(chan, ints);
5490 if (!res)
5491 if (tm.tm_min > 0)
5492 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5493 return res;
5496 /* Portuguese syntax */
5497 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5499 struct tm tm;
5500 int res = 0;
5501 int hour;
5502 localtime_r(&t,&tm);
5503 hour = tm.tm_hour;
5504 if (!res)
5505 res = ast_say_number(chan, hour, ints, lang, "f");
5506 if (tm.tm_min) {
5507 if (!res)
5508 res = wait_file(chan, ints, "digits/pt-e", lang);
5509 if (!res)
5510 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5511 } else {
5512 if (!res)
5513 res = wait_file(chan, ints, "digits/pt-hora", lang);
5514 if (tm.tm_hour != 1)
5515 if (!res)
5516 res = wait_file(chan, ints, "digits/pt-sss", lang);
5518 if (!res)
5519 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5520 return res;
5523 /* Taiwanese / Chinese syntax */
5524 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5526 struct tm tm;
5527 int res = 0;
5528 int hour, pm=0;
5529 localtime_r(&t,&tm);
5530 hour = tm.tm_hour;
5531 if (!hour)
5532 hour = 12;
5533 else if (hour == 12)
5534 pm = 1;
5535 else if (hour > 12) {
5536 hour -= 12;
5537 pm = 1;
5539 if (pm) {
5540 if (!res)
5541 res = ast_streamfile(chan, "digits/p-m", lang);
5542 } else {
5543 if (!res)
5544 res = ast_streamfile(chan, "digits/a-m", lang);
5546 if (!res)
5547 res = ast_waitstream(chan, ints);
5548 if (!res)
5549 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5550 if (!res)
5551 res = ast_streamfile(chan, "digits/oclock", lang);
5552 if (!res)
5553 res = ast_waitstream(chan, ints);
5554 if (!res)
5555 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5556 if (!res)
5557 res = ast_streamfile(chan, "digits/minute", lang);
5558 if (!res)
5559 res = ast_waitstream(chan, ints);
5560 return res;
5563 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5565 if (!strcasecmp(lang, "en") ) { /* English syntax */
5566 return(ast_say_datetime_en(chan, t, ints, lang));
5567 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5568 return(ast_say_datetime_de(chan, t, ints, lang));
5569 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5570 return(ast_say_datetime_fr(chan, t, ints, lang));
5571 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5572 return(ast_say_datetime_nl(chan, t, ints, lang));
5573 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5574 return(ast_say_datetime_pt(chan, t, ints, lang));
5575 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5576 return(ast_say_datetime_tw(chan, t, ints, lang));
5577 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5578 return(ast_say_datetime_gr(chan, t, ints, lang));
5581 /* Default to English */
5582 return(ast_say_datetime_en(chan, t, ints, lang));
5585 /* English syntax */
5586 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5588 struct tm tm;
5589 char fn[256];
5590 int res = 0;
5591 int hour, pm=0;
5592 localtime_r(&t,&tm);
5593 if (!res) {
5594 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5595 res = ast_streamfile(chan, fn, lang);
5596 if (!res)
5597 res = ast_waitstream(chan, ints);
5599 if (!res) {
5600 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5601 res = ast_streamfile(chan, fn, lang);
5602 if (!res)
5603 res = ast_waitstream(chan, ints);
5605 if (!res)
5606 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5608 hour = tm.tm_hour;
5609 if (!hour)
5610 hour = 12;
5611 else if (hour == 12)
5612 pm = 1;
5613 else if (hour > 12) {
5614 hour -= 12;
5615 pm = 1;
5617 if (!res)
5618 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5620 if (tm.tm_min > 9) {
5621 if (!res)
5622 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5623 } else if (tm.tm_min) {
5624 if (!res)
5625 res = ast_streamfile(chan, "digits/oh", lang);
5626 if (!res)
5627 res = ast_waitstream(chan, ints);
5628 if (!res)
5629 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5630 } else {
5631 if (!res)
5632 res = ast_streamfile(chan, "digits/oclock", lang);
5633 if (!res)
5634 res = ast_waitstream(chan, ints);
5636 if (pm) {
5637 if (!res)
5638 res = ast_streamfile(chan, "digits/p-m", lang);
5639 } else {
5640 if (!res)
5641 res = ast_streamfile(chan, "digits/a-m", lang);
5643 if (!res)
5644 res = ast_waitstream(chan, ints);
5645 if (!res)
5646 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5647 return res;
5650 /* German syntax */
5651 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5653 struct tm tm;
5654 int res = 0;
5655 localtime_r(&t,&tm);
5656 res = ast_say_date(chan, t, ints, lang);
5657 if (!res)
5658 ast_say_time(chan, t, ints, lang);
5659 return res;
5663 /* French syntax */
5664 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5666 struct tm tm;
5667 char fn[256];
5668 int res = 0;
5669 localtime_r(&t,&tm);
5671 if (!res)
5672 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5674 if (!res) {
5675 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5676 res = ast_streamfile(chan, fn, lang);
5677 if (!res)
5678 res = ast_waitstream(chan, ints);
5680 if (!res) {
5681 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5682 res = ast_streamfile(chan, fn, lang);
5683 if (!res)
5684 res = ast_waitstream(chan, ints);
5687 if (!res)
5688 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5689 if (!res)
5690 res = ast_streamfile(chan, "digits/oclock", lang);
5691 if (tm.tm_min > 0) {
5692 if (!res)
5693 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5695 if (!res)
5696 res = ast_waitstream(chan, ints);
5697 if (!res)
5698 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5699 return res;
5702 /* Dutch syntax */
5703 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5705 struct tm tm;
5706 int res = 0;
5707 localtime_r(&t,&tm);
5708 res = ast_say_date(chan, t, ints, lang);
5709 if (!res) {
5710 res = ast_streamfile(chan, "digits/nl-om", lang);
5711 if (!res)
5712 res = ast_waitstream(chan, ints);
5714 if (!res)
5715 ast_say_time(chan, t, ints, lang);
5716 return res;
5719 /* Portuguese syntax */
5720 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5722 struct tm tm;
5723 char fn[256];
5724 int res = 0;
5725 int hour, pm=0;
5726 localtime_r(&t,&tm);
5727 if (!res) {
5728 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5729 res = ast_streamfile(chan, fn, lang);
5730 if (!res)
5731 res = ast_waitstream(chan, ints);
5733 if (!res) {
5734 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5735 res = ast_streamfile(chan, fn, lang);
5736 if (!res)
5737 res = ast_waitstream(chan, ints);
5739 if (!res)
5740 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5742 hour = tm.tm_hour;
5743 if (!hour)
5744 hour = 12;
5745 else if (hour == 12)
5746 pm = 1;
5747 else if (hour > 12) {
5748 hour -= 12;
5749 pm = 1;
5751 if (!res)
5752 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5754 if (tm.tm_min > 9) {
5755 if (!res)
5756 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5757 } else if (tm.tm_min) {
5758 if (!res)
5759 res = ast_streamfile(chan, "digits/oh", lang);
5760 if (!res)
5761 res = ast_waitstream(chan, ints);
5762 if (!res)
5763 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5764 } else {
5765 if (!res)
5766 res = ast_streamfile(chan, "digits/oclock", lang);
5767 if (!res)
5768 res = ast_waitstream(chan, ints);
5770 if (pm) {
5771 if (!res)
5772 res = ast_streamfile(chan, "digits/p-m", lang);
5773 } else {
5774 if (!res)
5775 res = ast_streamfile(chan, "digits/a-m", lang);
5777 if (!res)
5778 res = ast_waitstream(chan, ints);
5779 if (!res)
5780 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5781 return res;
5784 /* Taiwanese / Chinese syntax */
5785 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5787 struct tm tm;
5788 char fn[256];
5789 int res = 0;
5790 int hour, pm=0;
5791 localtime_r(&t,&tm);
5792 if (!res)
5793 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5794 if (!res) {
5795 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5796 res = ast_streamfile(chan, fn, lang);
5797 if (!res)
5798 res = ast_waitstream(chan, ints);
5800 if (!res)
5801 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5802 if (!res) {
5803 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5804 res = ast_streamfile(chan, fn, lang);
5805 if (!res)
5806 res = ast_waitstream(chan, ints);
5809 hour = tm.tm_hour;
5810 if (!hour)
5811 hour = 12;
5812 else if (hour == 12)
5813 pm = 1;
5814 else if (hour > 12) {
5815 hour -= 12;
5816 pm = 1;
5818 if (pm) {
5819 if (!res)
5820 res = ast_streamfile(chan, "digits/p-m", lang);
5821 } else {
5822 if (!res)
5823 res = ast_streamfile(chan, "digits/a-m", lang);
5825 if (!res)
5826 res = ast_waitstream(chan, ints);
5827 if (!res)
5828 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5829 if (!res)
5830 res = ast_streamfile(chan, "digits/oclock", lang);
5831 if (!res)
5832 res = ast_waitstream(chan, ints);
5833 if (!res)
5834 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5835 if (!res)
5836 res = ast_streamfile(chan, "digits/minute", lang);
5837 if (!res)
5838 res = ast_waitstream(chan, ints);
5839 return res;
5842 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5844 if (!strcasecmp(lang, "en") ) { /* English syntax */
5845 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5846 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5847 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
5848 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5849 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
5852 /* Default to English */
5853 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5856 /* English syntax */
5857 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5859 int res=0;
5860 time_t nowt;
5861 int daydiff;
5862 struct tm tm;
5863 struct tm now;
5864 char fn[256];
5866 time(&nowt);
5868 localtime_r(&t,&tm);
5869 localtime_r(&nowt,&now);
5870 daydiff = now.tm_yday - tm.tm_yday;
5871 if ((daydiff < 0) || (daydiff > 6)) {
5872 /* Day of month and month */
5873 if (!res) {
5874 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5875 res = ast_streamfile(chan, fn, lang);
5876 if (!res)
5877 res = ast_waitstream(chan, ints);
5879 if (!res)
5880 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5882 } else if (daydiff) {
5883 /* Just what day of the week */
5884 if (!res) {
5885 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5886 res = ast_streamfile(chan, fn, lang);
5887 if (!res)
5888 res = ast_waitstream(chan, ints);
5890 } /* Otherwise, it was today */
5891 if (!res)
5892 res = ast_say_time(chan, t, ints, lang);
5893 return res;
5896 /* French syntax */
5897 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5899 int res=0;
5900 time_t nowt;
5901 int daydiff;
5902 struct tm tm;
5903 struct tm now;
5904 char fn[256];
5906 time(&nowt);
5908 localtime_r(&t,&tm);
5909 localtime_r(&nowt,&now);
5910 daydiff = now.tm_yday - tm.tm_yday;
5911 if ((daydiff < 0) || (daydiff > 6)) {
5912 /* Day of month and month */
5913 if (!res) {
5914 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5915 res = ast_streamfile(chan, fn, lang);
5916 if (!res)
5917 res = ast_waitstream(chan, ints);
5919 if (!res)
5920 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5922 } else if (daydiff) {
5923 /* Just what day of the week */
5924 if (!res) {
5925 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5926 res = ast_streamfile(chan, fn, lang);
5927 if (!res)
5928 res = ast_waitstream(chan, ints);
5930 } /* Otherwise, it was today */
5931 if (!res)
5932 res = ast_say_time(chan, t, ints, lang);
5933 return res;
5936 /* Portuguese syntax */
5937 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5939 int res=0;
5940 time_t nowt;
5941 int daydiff;
5942 struct tm tm;
5943 struct tm now;
5944 char fn[256];
5946 time(&nowt);
5948 localtime_r(&t,&tm);
5949 localtime_r(&nowt,&now);
5950 daydiff = now.tm_yday - tm.tm_yday;
5951 if ((daydiff < 0) || (daydiff > 6)) {
5952 /* Day of month and month */
5953 if (!res)
5954 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5955 if (!res)
5956 res = wait_file(chan, ints, "digits/pt-de", lang);
5957 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5958 if (!res)
5959 res = wait_file(chan, ints, fn, lang);
5961 } else if (daydiff) {
5962 /* Just what day of the week */
5963 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5964 if (!res)
5965 res = wait_file(chan, ints, fn, lang);
5966 } /* Otherwise, it was today */
5967 snprintf(fn, sizeof(fn), "digits/pt-ah");
5968 if (!res)
5969 res = wait_file(chan, ints, fn, lang);
5970 if (tm.tm_hour != 1)
5971 if (!res)
5972 res = wait_file(chan, ints, "digits/pt-sss", lang);
5973 if (!res)
5974 res = ast_say_time(chan, t, ints, lang);
5975 return res;
5979 /*********************************** GREEK SUPPORT ***************************************/
5983 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
5985 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
5986 int tmp;
5987 int left;
5988 int res;
5989 char fn[256] = "";
5991 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
5992 if (num < 5) {
5993 snprintf(fn, sizeof(fn), "digits/female-%d", num);
5994 res = wait_file(chan, ints, fn, lang);
5995 } else if (num < 13) {
5996 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
5997 } else if (num <100 ) {
5998 tmp = (num/10) * 10;
5999 left = num - tmp;
6000 snprintf(fn, sizeof(fn), "digits/%d", tmp);
6001 res = ast_streamfile(chan, fn, lang);
6002 if (!res)
6003 res = ast_waitstream(chan, ints);
6004 if (left)
6005 gr_say_number_female(left, chan, ints, lang);
6007 } else {
6008 return -1;
6010 return res;
6016 * A list of the files that you need to create
6017 -> digits/xilia = "xilia"
6018 -> digits/myrio = "ekatomyrio"
6019 -> digits/thousands = "xiliades"
6020 -> digits/millions = "ektatomyria"
6021 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6022 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6023 e,g 80 = "ogdonta"
6024 Here we must note that we use digits/tens/100 to utter "ekato"
6025 and digits/hundred-100 to utter "ekaton"
6026 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6027 "terakosia". Here again we use hundreds/1000 for "xilia"
6028 and digits/thousnds for "xiliades"
6031 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6033 int res = 0;
6034 char fn[256] = "";
6035 int i=0;
6038 if (!num) {
6039 snprintf(fn, sizeof(fn), "digits/0");
6040 res = ast_streamfile(chan, fn, chan->language);
6041 if (!res)
6042 return ast_waitstream(chan, ints);
6045 while(!res && num ) {
6046 i++;
6047 if (num < 13) {
6048 snprintf(fn, sizeof(fn), "digits/%d", num);
6049 num = 0;
6050 } else if (num <= 100) {
6051 /* 13 < num <= 100 */
6052 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6053 num -= ((num / 10) * 10);
6054 } else if (num < 200) {
6055 /* 100 < num < 200 */
6056 snprintf(fn, sizeof(fn), "digits/hundred-100");
6057 num -= ((num / 100) * 100);
6058 }else if (num < 1000) {
6059 /* 200 < num < 1000 */
6060 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6061 num -= ((num / 100) * 100);
6062 }else if (num < 2000){
6063 snprintf(fn, sizeof(fn), "digits/xilia");
6064 num -= ((num / 1000) * 1000);
6066 else {
6067 /* num > 1000 */
6068 if (num < 1000000) {
6069 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6070 if (res)
6071 return res;
6072 num = num % 1000;
6073 snprintf(fn, sizeof(fn), "digits/thousands");
6074 } else {
6075 if (num < 1000000000) { /* 1,000,000,000 */
6076 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6077 if (res)
6078 return res;
6079 num = num % 1000000;
6080 snprintf(fn, sizeof(fn), "digits/millions");
6081 } else {
6082 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6083 res = -1;
6087 if (!res) {
6088 if(!ast_streamfile(chan, fn, language)) {
6089 if ((audiofd > -1) && (ctrlfd > -1))
6090 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6091 else
6092 res = ast_waitstream(chan, ints);
6094 ast_stopstream(chan);
6097 return res;
6102 * The format is weekday - day - month -year
6104 * A list of the files that you need to create
6105 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6106 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6107 Attention the months are in
6108 "gekinh klhsh"
6112 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6114 struct tm tm;
6116 char fn[256];
6117 int res = 0;
6120 ast_localtime(&t,&tm,NULL);
6121 /* W E E K - D A Y */
6122 if (!res) {
6123 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6124 res = ast_streamfile(chan, fn, lang);
6125 if (!res)
6126 res = ast_waitstream(chan, ints);
6128 /* D A Y */
6129 if (!res) {
6130 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6132 /* M O N T H */
6133 if (!res) {
6134 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6135 res = ast_streamfile(chan, fn, lang);
6136 if (!res)
6137 res = ast_waitstream(chan, ints);
6139 /* Y E A R */
6140 if (!res)
6141 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6142 return res;
6147 /* A list of the files that you need to create
6148 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6149 * digits/kai : "KAI"
6150 * didgits : "h wra"
6151 * digits/p-m : "meta meshmbrias"
6152 * digits/a-m : "pro meshmbrias"
6155 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6158 struct tm tm;
6159 int res = 0;
6160 int hour, pm=0;
6162 localtime_r(&t,&tm);
6163 hour = tm.tm_hour;
6165 if (!hour)
6166 hour = 12;
6167 else if (hour == 12)
6168 pm = 1;
6169 else if (hour > 12) {
6170 hour -= 12;
6171 pm = 1;
6174 res = gr_say_number_female(hour, chan, ints, lang);
6175 if (tm.tm_min) {
6176 if (!res)
6177 res = ast_streamfile(chan, "digits/kai", lang);
6178 if (!res)
6179 res = ast_waitstream(chan, ints);
6180 if (!res)
6181 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6182 } else {
6183 if (!res)
6184 res = ast_streamfile(chan, "digits/hwra", lang);
6185 if (!res)
6186 res = ast_waitstream(chan, ints);
6188 if (pm) {
6189 if (!res)
6190 res = ast_streamfile(chan, "digits/p-m", lang);
6191 } else {
6192 if (!res)
6193 res = ast_streamfile(chan, "digits/a-m", lang);
6195 if (!res)
6196 res = ast_waitstream(chan, ints);
6197 return res;
6202 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6204 struct tm tm;
6205 char fn[256];
6206 int res = 0;
6207 localtime_r(&t,&tm);
6210 /* W E E K - D A Y */
6211 if (!res) {
6212 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6213 res = ast_streamfile(chan, fn, lang);
6214 if (!res)
6215 res = ast_waitstream(chan, ints);
6217 /* D A Y */
6218 if (!res) {
6219 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6221 /* M O N T H */
6222 if (!res) {
6223 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6224 res = ast_streamfile(chan, fn, lang);
6225 if (!res)
6226 res = ast_waitstream(chan, ints);
6229 res = ast_say_time_gr(chan, t, ints, lang);
6230 return res;
6233 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
6236 struct tm tm;
6237 int res=0, offset, sndoffset;
6238 char sndfile[256], nextmsg[256];
6240 if (!format)
6241 format = "AdBY 'digits/at' IMp";
6243 ast_localtime(&time,&tm,timezone);
6245 for (offset=0 ; format[offset] != '\0' ; offset++) {
6246 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6247 switch (format[offset]) {
6248 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6249 case '\'':
6250 /* Literal name of a sound file */
6251 sndoffset=0;
6252 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6253 sndfile[sndoffset] = format[offset];
6254 sndfile[sndoffset] = '\0';
6255 res = wait_file(chan,ints,sndfile,lang);
6256 break;
6257 case 'A':
6258 case 'a':
6259 /* Sunday - Saturday */
6260 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6261 res = wait_file(chan,ints,nextmsg,lang);
6262 break;
6263 case 'B':
6264 case 'b':
6265 case 'h':
6266 /* January - December */
6267 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6268 res = wait_file(chan,ints,nextmsg,lang);
6269 break;
6270 case 'd':
6271 case 'e':
6272 /* first - thirtyfirst */
6273 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6274 break;
6275 case 'Y':
6276 /* Year */
6278 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6279 break;
6280 case 'I':
6281 case 'l':
6282 /* 12-Hour */
6283 if (tm.tm_hour == 0)
6284 gr_say_number_female(12, chan, ints, lang);
6285 else if (tm.tm_hour > 12)
6286 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6287 else
6288 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6289 break;
6290 case 'H':
6291 case 'k':
6292 /* 24-Hour */
6293 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6294 break;
6295 case 'M':
6296 /* Minute */
6297 if (tm.tm_min) {
6298 if (!res)
6299 res = ast_streamfile(chan, "digits/kai", lang);
6300 if (!res)
6301 res = ast_waitstream(chan, ints);
6302 if (!res)
6303 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6304 } else {
6305 if (!res)
6306 res = ast_streamfile(chan, "digits/oclock", lang);
6307 if (!res)
6308 res = ast_waitstream(chan, ints);
6310 break;
6311 case 'P':
6312 case 'p':
6313 /* AM/PM */
6314 if (tm.tm_hour > 11)
6315 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6316 else
6317 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6318 res = wait_file(chan,ints,nextmsg,lang);
6319 break;
6320 case 'Q':
6321 /* Shorthand for "Today", "Yesterday", or ABdY */
6322 /* XXX As emphasized elsewhere, this should the native way in your
6323 * language to say the date, with changes in what you say, depending
6324 * upon how recent the date is. XXX */
6326 struct timeval now;
6327 struct tm tmnow;
6328 time_t beg_today, tt;
6330 gettimeofday(&now,NULL);
6331 tt = now.tv_sec;
6332 ast_localtime(&tt,&tmnow,timezone);
6333 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6334 /* In any case, it saves not having to do ast_mktime() */
6335 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6336 if (beg_today < time) {
6337 /* Today */
6338 res = wait_file(chan,ints, "digits/today",lang);
6339 } else if (beg_today - 86400 < time) {
6340 /* Yesterday */
6341 res = wait_file(chan,ints, "digits/yesterday",lang);
6342 } else {
6343 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6346 break;
6347 case 'q':
6348 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6349 /* XXX As emphasized elsewhere, this should the native way in your
6350 * language to say the date, with changes in what you say, depending
6351 * upon how recent the date is. XXX */
6353 struct timeval now;
6354 struct tm tmnow;
6355 time_t beg_today, tt;
6357 gettimeofday(&now,NULL);
6358 tt = now.tv_sec;
6359 ast_localtime(&tt,&tmnow,timezone);
6360 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6361 /* In any case, it saves not having to do ast_mktime() */
6362 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6363 if (beg_today < time) {
6364 /* Today */
6365 } else if ((beg_today - 86400) < time) {
6366 /* Yesterday */
6367 res = wait_file(chan,ints, "digits/yesterday",lang);
6368 } else if (beg_today - 86400 * 6 < time) {
6369 /* Within the last week */
6370 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6371 } else {
6372 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6375 break;
6376 case 'R':
6377 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6378 break;
6379 case 'S':
6380 /* Seconds */
6381 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6382 res = wait_file(chan,ints,nextmsg,lang);
6383 if (!res)
6384 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6385 if (!res)
6386 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6387 res = wait_file(chan,ints,nextmsg,lang);
6388 break;
6389 case 'T':
6390 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6391 break;
6392 case ' ':
6393 case ' ':
6394 /* Just ignore spaces and tabs */
6395 break;
6396 default:
6397 /* Unknown character */
6398 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6400 /* Jump out on DTMF */
6401 if (res) {
6402 break;
6405 return res;
6409 * remap the 'say' functions to use those in this file
6411 static void __attribute__((constructor)) __say_init(void)
6413 ast_say_number_full = say_number_full;
6414 ast_say_enumeration_full = say_enumeration_full;
6415 ast_say_digit_str_full = say_digit_str_full;
6416 ast_say_character_str_full = say_character_str_full;
6417 ast_say_phonetic_str_full = say_phonetic_str_full;
6418 ast_say_datetime = say_datetime;
6419 ast_say_time = say_time;
6420 ast_say_date = say_date;
6421 ast_say_datetime_from_now = say_datetime_from_now;
6422 ast_say_date_with_format = say_date_with_format;