update cleancount because the channel structure changed today
[asterisk-bristuff.git] / say.c
blob623a3425d3a0fcc92b1a683a36cfb013c3f810ea
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 2hundred: 2 hundred
1148 2thousands: 2 thousand
1149 thousands: plural of 'thousand'
1150 3sF 'Smichut forms (female)
1157 3s 'Smichut' forms (male)
1174 TODO: 've' should sometimed be 'hu':
1175 * before 'shtaym' (2, F)
1176 * before 'shnaym' (2, M)
1177 * before 'shlosha' (3, M)
1178 * before 'shmone' (8, M)
1179 * before 'shlosim' (30)
1180 * before 'shmonim' (80)
1182 What about:
1183 'sheva' (7, F)?
1184 'tesha' (9, F)?
1186 #define SAY_NUM_BUF_SIZE 256
1187 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1188 const char *ints, const char *language, const char *options,
1189 int audiofd, int ctrlfd)
1191 int res = 0;
1192 int state = 0; /* no need to save anything */
1193 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1194 char fn[SAY_NUM_BUF_SIZE] = "";
1195 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1196 "num: %d, options=\"%s\"\n",
1197 num, options
1199 if (!num)
1200 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1202 if (options && !strncasecmp(options, "f",1))
1203 mf = -1;
1205 /* Do we have work to do? */
1206 while(!res && (num || (state>0) )) {
1207 /* first type of work: play a second sound. In this loop
1208 * we can only play one sound file at a time. Thus playing
1209 * a second one requires repeating the loop just for the
1210 * second file. The variable 'state' remembers where we were.
1211 * state==0 is the normal mode and it means that we continue
1212 * to check if the number num has yet anything left.
1214 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1215 "state=%d, options=\"%s\", mf=%d\n",
1216 num, state, options, mf
1218 if (state==1) {
1219 snprintf(fn, sizeof(fn), "digits/hundred");
1220 state = 0;
1221 } else if (state==2) {
1222 snprintf(fn, sizeof(fn), "digits/ve");
1223 state = 0;
1224 } else if (state==3) {
1225 snprintf(fn, sizeof(fn), "digits/thousands");
1226 state=0;
1227 } else if (num <21) {
1228 if (mf < 0)
1229 snprintf(fn, sizeof(fn), "digits/%dF", num);
1230 else
1231 snprintf(fn, sizeof(fn), "digits/%d", num);
1232 num = 0;
1233 } else if (num < 100) {
1234 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1235 num = num % 10;
1236 if (num>0) state=2;
1237 } else if (num < 200) {
1238 snprintf(fn, sizeof(fn), "digits/hundred");
1239 num = num - 100;
1240 } else if (num < 300) {
1241 snprintf(fn, sizeof(fn), "digits/hundred");
1242 num = num - 100;
1243 } else if (num < 1000) {
1244 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1245 state=1;
1246 num = num % 100;
1247 } else if (num < 2000) {
1248 snprintf(fn, sizeof(fn), "digits/thousand");
1249 num = num - 1000;
1250 } else if (num < 3000) {
1251 snprintf(fn, sizeof(fn), "digits/2thousand");
1252 num = num - 2000;
1253 if (num>0) state=2;
1254 } else if (num < 20000) {
1255 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1256 num = num % 1000;
1257 state=3;
1258 } else if (num < 1000000) {
1259 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1260 if (res)
1261 return res;
1262 snprintf(fn, sizeof(fn), "digits/thousand");
1263 num = num % 1000;
1264 } else if (num < 1000000000) {
1265 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1266 if (res)
1267 return res;
1268 snprintf(fn, sizeof(fn), "digits/million");
1269 num = num % 1000000;
1270 } else {
1271 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1272 res = -1;
1274 if (!res) {
1275 if(!ast_streamfile(chan, fn, language)) {
1276 if ((audiofd > -1) && (ctrlfd > -1))
1277 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1278 else
1279 res = ast_waitstream(chan, ints);
1281 ast_stopstream(chan);
1284 return res;
1287 /*! \brief ast_say_number_full_it: Italian */
1288 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1290 int res = 0;
1291 int playh = 0;
1292 int tempnum = 0;
1293 char fn[256] = "";
1295 if (!num)
1296 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1299 Italian support
1301 Like english, numbers up to 20 are a single 'word', and others
1302 compound, but with exceptions.
1303 For example 21 is not twenty-one, but there is a single word in 'it'.
1304 Idem for 28 (ie when a the 2nd part of a compund number
1305 starts with a vowel)
1307 There are exceptions also for hundred, thousand and million.
1308 In english 100 = one hundred, 200 is two hundred.
1309 In italian 100 = cento , like to say hundred (without one),
1310 200 and more are like english.
1312 Same applies for thousand:
1313 1000 is one thousand in en, 2000 is two thousand.
1314 In it we have 1000 = mille , 2000 = 2 mila
1316 For million(s) we use the plural, if more than one
1317 Also, one million is abbreviated in it, like on-million,
1318 or 'un milione', not 'uno milione'.
1319 So the right file is provided.
1322 while(!res && (num || playh)) {
1323 if (num < 0) {
1324 snprintf(fn, sizeof(fn), "digits/minus");
1325 if ( num > INT_MIN ) {
1326 num = -num;
1327 } else {
1328 num = 0;
1330 } else if (playh) {
1331 snprintf(fn, sizeof(fn), "digits/hundred");
1332 playh = 0;
1333 } else if (num < 20) {
1334 snprintf(fn, sizeof(fn), "digits/%d", num);
1335 num = 0;
1336 } else if (num == 21) {
1337 snprintf(fn, sizeof(fn), "digits/%d", num);
1338 num = 0;
1339 } else if (num == 28) {
1340 snprintf(fn, sizeof(fn), "digits/%d", num);
1341 num = 0;
1342 } else if (num == 31) {
1343 snprintf(fn, sizeof(fn), "digits/%d", num);
1344 num = 0;
1345 } else if (num == 38) {
1346 snprintf(fn, sizeof(fn), "digits/%d", num);
1347 num = 0;
1348 } else if (num == 41) {
1349 snprintf(fn, sizeof(fn), "digits/%d", num);
1350 num = 0;
1351 } else if (num == 48) {
1352 snprintf(fn, sizeof(fn), "digits/%d", num);
1353 num = 0;
1354 } else if (num == 51) {
1355 snprintf(fn, sizeof(fn), "digits/%d", num);
1356 num = 0;
1357 } else if (num == 58) {
1358 snprintf(fn, sizeof(fn), "digits/%d", num);
1359 num = 0;
1360 } else if (num == 61) {
1361 snprintf(fn, sizeof(fn), "digits/%d", num);
1362 num = 0;
1363 } else if (num == 68) {
1364 snprintf(fn, sizeof(fn), "digits/%d", num);
1365 num = 0;
1366 } else if (num == 71) {
1367 snprintf(fn, sizeof(fn), "digits/%d", num);
1368 num = 0;
1369 } else if (num == 78) {
1370 snprintf(fn, sizeof(fn), "digits/%d", num);
1371 num = 0;
1372 } else if (num == 81) {
1373 snprintf(fn, sizeof(fn), "digits/%d", num);
1374 num = 0;
1375 } else if (num == 88) {
1376 snprintf(fn, sizeof(fn), "digits/%d", num);
1377 num = 0;
1378 } else if (num == 91) {
1379 snprintf(fn, sizeof(fn), "digits/%d", num);
1380 num = 0;
1381 } else if (num == 98) {
1382 snprintf(fn, sizeof(fn), "digits/%d", num);
1383 num = 0;
1384 } else if (num < 100) {
1385 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1386 num -= ((num / 10) * 10);
1387 } else {
1388 if (num < 1000) {
1389 if ((num / 100) > 1) {
1390 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1391 playh++;
1392 } else {
1393 snprintf(fn, sizeof(fn), "digits/hundred");
1395 num -= ((num / 100) * 100);
1396 } else {
1397 if (num < 1000000) { /* 1,000,000 */
1398 if ((num/1000) > 1)
1399 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1400 if (res)
1401 return res;
1402 tempnum = num;
1403 num = num % 1000;
1404 if ((tempnum / 1000) < 2)
1405 snprintf(fn, sizeof(fn), "digits/thousand");
1406 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1407 snprintf(fn, sizeof(fn), "digits/thousands");
1408 } else {
1409 if (num < 1000000000) { /* 1,000,000,000 */
1410 if ((num / 1000000) > 1)
1411 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1412 if (res)
1413 return res;
1414 tempnum = num;
1415 num = num % 1000000;
1416 if ((tempnum / 1000000) < 2)
1417 snprintf(fn, sizeof(fn), "digits/million");
1418 else
1419 snprintf(fn, sizeof(fn), "digits/millions");
1420 } else {
1421 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1422 res = -1;
1427 if (!res) {
1428 if(!ast_streamfile(chan, fn, language)) {
1429 if ((audiofd > -1) && (ctrlfd > -1))
1430 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1431 else
1432 res = ast_waitstream(chan, ints);
1434 ast_stopstream(chan);
1437 return res;
1440 /*! \brief ast_say_number_full_nl: dutch syntax */
1441 /* New files: digits/nl-en
1443 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1445 int res = 0;
1446 int playh = 0;
1447 int units = 0;
1448 char fn[256] = "";
1449 if (!num)
1450 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1451 while (!res && (num || playh )) {
1452 if (num < 0) {
1453 snprintf(fn, sizeof(fn), "digits/minus");
1454 if ( num > INT_MIN ) {
1455 num = -num;
1456 } else {
1457 num = 0;
1459 } else if (playh) {
1460 snprintf(fn, sizeof(fn), "digits/hundred");
1461 playh = 0;
1462 } else if (num < 20) {
1463 snprintf(fn, sizeof(fn), "digits/%d", num);
1464 num = 0;
1465 } else if (num < 100) {
1466 units = num % 10;
1467 if (units > 0) {
1468 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1469 if (res)
1470 return res;
1471 num = num - units;
1472 snprintf(fn, sizeof(fn), "digits/nl-en");
1473 } else {
1474 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1475 num = 0;
1477 } else {
1478 if (num < 1000) {
1479 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1480 playh++;
1481 num -= ((num / 100) * 100);
1482 } else {
1483 if (num < 1000000) { /* 1,000,000 */
1484 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1485 if (res)
1486 return res;
1487 num = num % 1000;
1488 snprintf(fn, sizeof(fn), "digits/thousand");
1489 } else {
1490 if (num < 1000000000) { /* 1,000,000,000 */
1491 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1492 if (res)
1493 return res;
1494 num = num % 1000000;
1495 snprintf(fn, sizeof(fn), "digits/million");
1496 } else {
1497 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1498 res = -1;
1504 if (!res) {
1505 if(!ast_streamfile(chan, fn, language)) {
1506 if ((audiofd > -1) && (ctrlfd > -1))
1507 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1508 else
1509 res = ast_waitstream(chan, ints);
1511 ast_stopstream(chan);
1514 return res;
1517 /*! \brief ast_say_number_full_no: Norwegian syntax */
1518 /* New files:
1519 In addition to American English, the following sounds are required: "and", "1N"
1521 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)
1523 int res = 0;
1524 int playh = 0;
1525 int playa = 0;
1526 int cn = 1; /* +1 = commune; -1 = neuter */
1527 char fn[256] = "";
1529 if (!num)
1530 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1532 if (options && !strncasecmp(options, "n",1)) cn = -1;
1534 while(!res && (num || playh || playa )) {
1535 /* The grammar for Norwegian numbers is the same as for English except
1536 * for the following:
1537 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1538 * "and" before the last two digits, i.e. 2034 is "two thousand and
1539 * thirty-four" and 1000012 is "one million and twelve".
1541 if (num < 0) {
1542 snprintf(fn, sizeof(fn), "digits/minus");
1543 if ( num > INT_MIN ) {
1544 num = -num;
1545 } else {
1546 num = 0;
1548 } else if (playh) {
1549 snprintf(fn, sizeof(fn), "digits/hundred");
1550 playh = 0;
1551 } else if (playa) {
1552 snprintf(fn, sizeof(fn), "digits/and");
1553 playa = 0;
1554 } else if (num == 1 && cn == -1) {
1555 snprintf(fn, sizeof(fn), "digits/1N");
1556 num = 0;
1557 } else if (num < 20) {
1558 snprintf(fn, sizeof(fn), "digits/%d", num);
1559 num = 0;
1560 } else if (num < 100) {
1561 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1562 num -= ((num / 10) * 10);
1563 } else if (num < 1000) {
1564 int hundreds = num / 100;
1565 if (hundreds == 1)
1566 snprintf(fn, sizeof(fn), "digits/1N");
1567 else
1568 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1570 playh++;
1571 num -= 100 * hundreds;
1572 if (num)
1573 playa++;
1574 } else if (num < 1000000) {
1575 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1576 if (res)
1577 return res;
1578 snprintf(fn, sizeof(fn), "digits/thousand");
1579 num = num % 1000;
1580 if (num && num < 100)
1581 playa++;
1582 } else if (num < 1000000000) {
1583 int millions = num / 1000000;
1584 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1585 if (res)
1586 return res;
1587 snprintf(fn, sizeof(fn), "digits/million");
1588 num = num % 1000000;
1589 if (num && num < 100)
1590 playa++;
1591 } else {
1592 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1593 res = -1;
1596 if (!res) {
1597 if(!ast_streamfile(chan, fn, language)) {
1598 if ((audiofd > -1) && (ctrlfd > -1))
1599 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1600 else
1601 res = ast_waitstream(chan, ints);
1603 ast_stopstream(chan);
1606 return res;
1609 typedef struct {
1610 char *separator_dziesiatek;
1611 char *cyfry[10];
1612 char *cyfry2[10];
1613 char *setki[10];
1614 char *dziesiatki[10];
1615 char *nastki[10];
1616 char *rzedy[3][3];
1617 } odmiana;
1619 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1621 if (rzad==0)
1622 return "";
1624 if (i==1)
1625 return odm->rzedy[rzad - 1][0];
1626 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1627 return odm->rzedy[rzad - 1][1];
1628 else
1629 return odm->rzedy[rzad - 1][2];
1632 static char* pl_append(char* buffer, char* str)
1634 strcpy(buffer, str);
1635 buffer += strlen(str);
1636 return buffer;
1639 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1641 char file_name[255] = "digits/";
1642 strcat(file_name, fn);
1643 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1644 if (!ast_streamfile(chan, file_name, language)) {
1645 if ((audiofd > -1) && (ctrlfd > -1))
1646 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1647 else
1648 ast_waitstream(chan, ints);
1650 ast_stopstream(chan);
1653 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1655 /* Initialise variables to allow compilation on Debian-stable, etc */
1656 int m1000E6 = 0;
1657 int i1000E6 = 0;
1658 int m1000E3 = 0;
1659 int i1000E3 = 0;
1660 int m1000 = 0;
1661 int i1000 = 0;
1662 int m100 = 0;
1663 int i100 = 0;
1665 if (i == 0 && rzad > 0) {
1666 return;
1668 if (i == 0) {
1669 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1670 return;
1673 m1000E6 = i % 1000000000;
1674 i1000E6 = i / 1000000000;
1676 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1678 m1000E3 = m1000E6 % 1000000;
1679 i1000E3 = m1000E6 / 1000000;
1681 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1683 m1000 = m1000E3 % 1000;
1684 i1000 = m1000E3 / 1000;
1686 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1688 m100 = m1000 % 100;
1689 i100 = m1000 / 100;
1691 if (i100>0)
1692 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1694 if ( m100 > 0 && m100 <=9 ) {
1695 if (m1000>0)
1696 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1697 else
1698 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1699 } else if (m100 % 10 == 0) {
1700 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1701 } else if (m100 <= 19 ) {
1702 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1703 } else if (m100 != 0) {
1704 if (odm->separator_dziesiatek[0]==' ') {
1705 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1706 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1707 } else {
1708 char buf[10];
1709 char *b = buf;
1710 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1711 b = pl_append(b, odm->separator_dziesiatek);
1712 b = pl_append(b, odm->cyfry2[m100 % 10]);
1713 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1717 if (rzad > 0) {
1718 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1722 /* ast_say_number_full_pl: Polish syntax */
1723 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)
1725 Sounds needed:
1726 0 zero
1727 1 jeden
1728 10 dziesiec
1729 100 sto
1730 1000 tysiac
1731 1000000 milion
1732 1000000000 miliard
1733 1000000000.2 miliardy
1734 1000000000.5 miliardow
1735 1000000.2 miliony
1736 1000000.5 milionow
1737 1000.2 tysiace
1738 1000.5 tysiecy
1739 100m stu
1740 10m dziesieciu
1741 11 jedenascie
1742 11m jedenastu
1743 12 dwanascie
1744 12m dwunastu
1745 13 trzynascie
1746 13m trzynastu
1747 14 czternascie
1748 14m czternastu
1749 15 pietnascie
1750 15m pietnastu
1751 16 szesnascie
1752 16m szesnastu
1753 17 siedemnascie
1754 17m siedemnastu
1755 18 osiemnascie
1756 18m osiemnastu
1757 19 dziewietnascie
1758 19m dziewietnastu
1759 1z jedna
1760 2 dwa
1761 20 dwadziescia
1762 200 dwiescie
1763 200m dwustu
1764 20m dwudziestu
1765 2-1m dwaj
1766 2-2m dwoch
1767 2z dwie
1768 3 trzy
1769 30 trzydziesci
1770 300 trzysta
1771 300m trzystu
1772 30m trzydziestu
1773 3-1m trzej
1774 3-2m trzech
1775 4 cztery
1776 40 czterdziesci
1777 400 czterysta
1778 400m czterystu
1779 40m czterdziestu
1780 4-1m czterej
1781 4-2m czterech
1782 5 piec
1783 50 piecdziesiat
1784 500 piecset
1785 500m pieciuset
1786 50m piedziesieciu
1787 5m pieciu
1788 6 szesc
1789 60 szescdziesiat
1790 600 szescset
1791 600m szesciuset
1792 60m szescdziesieciu
1793 6m szesciu
1794 7 siedem
1795 70 siedemdziesiat
1796 700 siedemset
1797 700m siedmiuset
1798 70m siedemdziesieciu
1799 7m siedmiu
1800 8 osiem
1801 80 osiemdziesiat
1802 800 osiemset
1803 800m osmiuset
1804 80m osiemdziesieciu
1805 8m osmiu
1806 9 dziewiec
1807 90 dziewiecdziesiat
1808 900 dziewiecset
1809 900m dziewieciuset
1810 90m dziewiedziesieciu
1811 9m dziewieciu
1812 and combinations of eg.: 20_1, 30m_3m, etc...
1816 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1818 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1820 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1822 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1824 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1826 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1828 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1830 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1832 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1834 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1836 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1838 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1840 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1842 /* Initialise variables to allow compilation on Debian-stable, etc */
1843 odmiana *o;
1845 static odmiana *odmiana_nieosobowa = NULL;
1846 static odmiana *odmiana_meska = NULL;
1847 static odmiana *odmiana_zenska = NULL;
1849 if (odmiana_nieosobowa == NULL) {
1850 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1852 odmiana_nieosobowa->separator_dziesiatek = " ";
1854 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1855 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1856 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1857 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1858 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1859 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1862 if (odmiana_zenska == NULL) {
1863 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1865 odmiana_zenska->separator_dziesiatek = " ";
1867 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1868 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1869 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1870 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1871 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1872 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1875 if (odmiana_meska == NULL) {
1876 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1878 odmiana_meska->separator_dziesiatek = " ";
1880 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1881 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1882 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1883 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1884 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1885 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1888 if (options) {
1889 if (strncasecmp(options, "f", 1) == 0)
1890 o = odmiana_zenska;
1891 else if (strncasecmp(options, "m", 1) == 0)
1892 o = odmiana_meska;
1893 else
1894 o = odmiana_nieosobowa;
1895 } else
1896 o = odmiana_nieosobowa;
1898 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1899 return 0;
1902 /* ast_say_number_full_pt: Portuguese syntax */
1903 /* Extra sounds needed: */
1904 /* For feminin all sound files end with F */
1905 /* 100E for 100+ something */
1906 /* 1000000S for plural */
1907 /* pt-e for 'and' */
1908 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)
1910 int res = 0;
1911 int playh = 0;
1912 int mf = 1; /* +1 = male; -1 = female */
1913 char fn[256] = "";
1915 if (!num)
1916 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1918 if (options && !strncasecmp(options, "f",1))
1919 mf = -1;
1921 while(!res && num ) {
1922 if (num < 0) {
1923 snprintf(fn, sizeof(fn), "digits/minus");
1924 if ( num > INT_MIN ) {
1925 num = -num;
1926 } else {
1927 num = 0;
1929 } else if (num < 20) {
1930 if ((num == 1 || num == 2) && (mf < 0))
1931 snprintf(fn, sizeof(fn), "digits/%dF", num);
1932 else
1933 snprintf(fn, sizeof(fn), "digits/%d", num);
1934 num = 0;
1935 } else if (num < 100) {
1936 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1937 if (num % 10)
1938 playh = 1;
1939 num = num % 10;
1940 } else if (num < 1000) {
1941 if (num == 100)
1942 snprintf(fn, sizeof(fn), "digits/100");
1943 else if (num < 200)
1944 snprintf(fn, sizeof(fn), "digits/100E");
1945 else {
1946 if (mf < 0 && num > 199)
1947 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1948 else
1949 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1950 if (num % 100)
1951 playh = 1;
1953 num = num % 100;
1954 } else if (num < 1000000) {
1955 if (num > 1999) {
1956 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1957 if (res)
1958 return res;
1960 snprintf(fn, sizeof(fn), "digits/1000");
1961 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1962 playh = 1;
1963 num = num % 1000;
1964 } else if (num < 1000000000) {
1965 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1966 if (res)
1967 return res;
1968 if (num < 2000000)
1969 snprintf(fn, sizeof(fn), "digits/1000000");
1970 else
1971 snprintf(fn, sizeof(fn), "digits/1000000S");
1973 if ((num % 1000000) &&
1974 /* no thousands */
1975 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1976 /* no hundreds and below */
1977 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1978 playh = 1;
1979 num = num % 1000000;
1981 if (!res) {
1982 if (!ast_streamfile(chan, fn, language)) {
1983 if ((audiofd > -1) && (ctrlfd > -1))
1984 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1985 else
1986 res = ast_waitstream(chan, ints);
1988 ast_stopstream(chan);
1990 if (!res && playh) {
1991 res = wait_file(chan, ints, "digits/pt-e", language);
1992 ast_stopstream(chan);
1993 playh = 0;
1996 return res;
1999 /*! \brief ast_say_number_full_se: Swedish syntax */
2000 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)
2002 int res = 0;
2003 int playh = 0;
2004 char fn[256] = "";
2005 int cn = 1; /* +1 = commune; -1 = neuter */
2006 if (!num)
2007 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2008 if (options && !strncasecmp(options, "n",1)) cn = -1;
2010 while(!res && (num || playh)) {
2011 if (num < 0) {
2012 snprintf(fn, sizeof(fn), "digits/minus");
2013 if ( num > INT_MIN ) {
2014 num = -num;
2015 } else {
2016 num = 0;
2018 } else if (playh) {
2019 snprintf(fn, sizeof(fn), "digits/hundred");
2020 playh = 0;
2021 } else if (num < 20) {
2022 snprintf(fn, sizeof(fn), "digits/%d", num);
2023 num = 0;
2024 } else if (num < 100) {
2025 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2026 num -= ((num / 10) * 10);
2027 } else if (num == 1 && cn == -1) { /* En eller ett? */
2028 snprintf(fn, sizeof(fn), "digits/1N");
2029 num = 0;
2030 } else {
2031 if (num < 1000){
2032 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2033 playh++;
2034 num -= ((num / 100) * 100);
2035 } else {
2036 if (num < 1000000) { /* 1,000,000 */
2037 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2038 if (res) {
2039 return res;
2041 num = num % 1000;
2042 snprintf(fn, sizeof(fn), "digits/thousand");
2043 } else {
2044 if (num < 1000000000) { /* 1,000,000,000 */
2045 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2046 if (res) {
2047 return res;
2049 num = num % 1000000;
2050 snprintf(fn, sizeof(fn), "digits/million");
2051 } else {
2052 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2053 res = -1;
2058 if (!res) {
2059 if(!ast_streamfile(chan, fn, language)) {
2060 if ((audiofd > -1) && (ctrlfd > -1))
2061 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2062 else
2063 res = ast_waitstream(chan, ints);
2064 ast_stopstream(chan);
2068 return res;
2071 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2072 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2074 int res = 0;
2075 int playh = 0;
2076 char fn[256] = "";
2077 if (!num)
2078 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2080 while(!res && (num || playh)) {
2081 if (num < 0) {
2082 snprintf(fn, sizeof(fn), "digits/minus");
2083 if ( num > INT_MIN ) {
2084 num = -num;
2085 } else {
2086 num = 0;
2088 } else if (playh) {
2089 snprintf(fn, sizeof(fn), "digits/hundred");
2090 playh = 0;
2091 } else if (num < 10) {
2092 snprintf(fn, sizeof(fn), "digits/%d", num);
2093 num = 0;
2094 } else if (num < 100) {
2095 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2096 num -= ((num / 10) * 10);
2097 } else {
2098 if (num < 1000){
2099 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2100 playh++;
2101 num -= ((num / 100) * 100);
2102 } else {
2103 if (num < 1000000) { /* 1,000,000 */
2104 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2105 if (res)
2106 return res;
2107 num = num % 1000;
2108 snprintf(fn, sizeof(fn), "digits/thousand");
2109 } else {
2110 if (num < 1000000000) { /* 1,000,000,000 */
2111 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2112 if (res)
2113 return res;
2114 num = num % 1000000;
2115 snprintf(fn, sizeof(fn), "digits/million");
2116 } else {
2117 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2118 res = -1;
2123 if (!res) {
2124 if(!ast_streamfile(chan, fn, language)) {
2125 if ((audiofd > -1) && (ctrlfd > -1))
2126 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2127 else
2128 res = ast_waitstream(chan, ints);
2130 ast_stopstream(chan);
2133 return res;
2137 /*! \brief determine last digits for thousands/millions (ru) */
2138 static int get_lastdigits_ru(int num) {
2139 if (num < 20) {
2140 return num;
2141 } else if (num < 100) {
2142 return get_lastdigits_ru(num % 10);
2143 } else if (num < 1000) {
2144 return get_lastdigits_ru(num % 100);
2146 return 0; /* number too big */
2150 /*! \brief ast_say_number_full_ru: Russian syntax */
2151 /*! \brief additional files:
2152 n00.gsm (one hundred, two hundred, ...)
2153 thousand.gsm
2154 million.gsm
2155 thousands-i.gsm (tisyachi)
2156 million-a.gsm (milliona)
2157 thousands.gsm
2158 millions.gsm
2159 1f.gsm (odna)
2160 2f.gsm (dve)
2162 where 'n' from 1 to 9
2164 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)
2166 int res = 0;
2167 int lastdigits = 0;
2168 char fn[256] = "";
2169 if (!num)
2170 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2172 while(!res && (num)) {
2173 if (num < 0) {
2174 snprintf(fn, sizeof(fn), "digits/minus");
2175 if ( num > INT_MIN ) {
2176 num = -num;
2177 } else {
2178 num = 0;
2180 } else if (num < 20) {
2181 if(options && strlen(options) == 1 && num < 3) {
2182 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2183 } else {
2184 snprintf(fn, sizeof(fn), "digits/%d", num);
2186 num = 0;
2187 } else if (num < 100) {
2188 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2189 num %= 10;
2190 } else if (num < 1000){
2191 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2192 num %= 100;
2193 } else if (num < 1000000) { /* 1,000,000 */
2194 lastdigits = get_lastdigits_ru(num / 1000);
2195 /* say thousands */
2196 if (lastdigits < 3) {
2197 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2198 } else {
2199 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2201 if (res)
2202 return res;
2203 if (lastdigits == 1) {
2204 snprintf(fn, sizeof(fn), "digits/thousand");
2205 } else if (lastdigits > 1 && lastdigits < 5) {
2206 snprintf(fn, sizeof(fn), "digits/thousands-i");
2207 } else {
2208 snprintf(fn, sizeof(fn), "digits/thousands");
2210 num %= 1000;
2211 } else if (num < 1000000000) { /* 1,000,000,000 */
2212 lastdigits = get_lastdigits_ru(num / 1000000);
2213 /* say millions */
2214 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2215 if (res)
2216 return res;
2217 if (lastdigits == 1) {
2218 snprintf(fn, sizeof(fn), "digits/million");
2219 } else if (lastdigits > 1 && lastdigits < 5) {
2220 snprintf(fn, sizeof(fn), "digits/million-a");
2221 } else {
2222 snprintf(fn, sizeof(fn), "digits/millions");
2224 num %= 1000000;
2225 } else {
2226 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2227 res = -1;
2229 if (!res) {
2230 if (!ast_streamfile(chan, fn, language)) {
2231 if ((audiofd > -1) && (ctrlfd > -1))
2232 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2233 else
2234 res = ast_waitstream(chan, ints);
2236 ast_stopstream(chan);
2239 return res;
2243 /*! \brief ast_say_enumeration_full: call language-specific functions */
2244 /* Called from AGI */
2245 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2247 if (!strcasecmp(language,"en") ) { /* English syntax */
2248 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2249 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2250 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2251 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2252 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2255 /* Default to english */
2256 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2259 /*! \brief ast_say_enumeration_full_en: English syntax */
2260 /* This is the default syntax, if no other syntax defined in this file is used */
2261 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2263 int res = 0, t = 0;
2264 char fn[256] = "";
2266 while(!res && num) {
2267 if (num < 0) {
2268 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2269 if ( num > INT_MIN ) {
2270 num = -num;
2271 } else {
2272 num = 0;
2274 } else if (num < 20) {
2275 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2276 num = 0;
2277 } else if (num < 100) {
2278 int tens = num / 10;
2279 num = num % 10;
2280 if (num == 0) {
2281 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2282 } else {
2283 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2285 } else if (num < 1000) {
2286 int hundreds = num / 100;
2287 num = num % 100;
2288 if (hundreds > 1 || t == 1) {
2289 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2291 if (res)
2292 return res;
2293 if (num) {
2294 snprintf(fn, sizeof(fn), "digits/hundred");
2295 } else {
2296 snprintf(fn, sizeof(fn), "digits/h-hundred");
2298 } else if (num < 1000000) {
2299 int thousands = num / 1000;
2300 num = num % 1000;
2301 if (thousands > 1 || t == 1) {
2302 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2304 if (res)
2305 return res;
2306 if (num) {
2307 snprintf(fn, sizeof(fn), "digits/thousand");
2308 } else {
2309 snprintf(fn, sizeof(fn), "digits/h-thousand");
2311 t = 1;
2312 } else if (num < 1000000000) {
2313 int millions = num / 1000000;
2314 num = num % 1000000;
2315 t = 1;
2316 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2317 if (res)
2318 return res;
2319 if (num) {
2320 snprintf(fn, sizeof(fn), "digits/million");
2321 } else {
2322 snprintf(fn, sizeof(fn), "digits/h-million");
2324 } else if (num < INT_MAX) {
2325 int billions = num / 1000000000;
2326 num = num % 1000000000;
2327 t = 1;
2328 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2329 if (res)
2330 return res;
2331 if (num) {
2332 snprintf(fn, sizeof(fn), "digits/billion");
2333 } else {
2334 snprintf(fn, sizeof(fn), "digits/h-billion");
2336 } else if (num == INT_MAX) {
2337 snprintf(fn, sizeof(fn), "digits/h-last");
2338 num = 0;
2339 } else {
2340 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2341 res = -1;
2344 if (!res) {
2345 if (!ast_streamfile(chan, fn, language)) {
2346 if ((audiofd > -1) && (ctrlfd > -1)) {
2347 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2348 } else {
2349 res = ast_waitstream(chan, ints);
2352 ast_stopstream(chan);
2355 return res;
2358 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2359 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)
2361 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2362 int res = 0, t = 0;
2363 char fn[256] = "", fna[256] = "";
2364 char *gender;
2366 if (options && !strncasecmp(options, "f",1)) {
2367 gender = "F";
2368 } else if (options && !strncasecmp(options, "n",1)) {
2369 gender = "N";
2370 } else {
2371 gender = "";
2374 if (!num)
2375 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2377 while(!res && num) {
2378 if (num < 0) {
2379 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2380 if ( num > INT_MIN ) {
2381 num = -num;
2382 } else {
2383 num = 0;
2385 } else if (num < 100 && t) {
2386 snprintf(fn, sizeof(fn), "digits/and");
2387 t = 0;
2388 } else if (num < 20) {
2389 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2390 num = 0;
2391 } else if (num < 100) {
2392 int ones = num % 10;
2393 if (ones) {
2394 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2395 num -= ones;
2396 } else {
2397 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2398 num = 0;
2400 } else if (num == 100 && t == 0) {
2401 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2402 num = 0;
2403 } else if (num < 1000) {
2404 int hundreds = num / 100;
2405 num = num % 100;
2406 if (hundreds == 1) {
2407 snprintf(fn, sizeof(fn), "digits/1N");
2408 } else {
2409 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2411 if (num) {
2412 snprintf(fna, sizeof(fna), "digits/hundred");
2413 } else {
2414 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2416 t = 1;
2417 } else if (num < 1000000) {
2418 int thousands = num / 1000;
2419 num = num % 1000;
2420 if (thousands == 1) {
2421 if (num) {
2422 snprintf(fn, sizeof(fn), "digits/1N");
2423 snprintf(fna, sizeof(fna), "digits/thousand");
2424 } else {
2425 if (t) {
2426 snprintf(fn, sizeof(fn), "digits/1N");
2427 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2428 } else {
2429 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2432 } else {
2433 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2434 if (res) {
2435 return res;
2437 if (num) {
2438 snprintf(fn, sizeof(fn), "digits/thousand");
2439 } else {
2440 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2443 t = 1;
2444 } else if (num < 1000000000) {
2445 int millions = num / 1000000;
2446 num = num % 1000000;
2447 if (millions == 1) {
2448 if (num) {
2449 snprintf(fn, sizeof(fn), "digits/1F");
2450 snprintf(fna, sizeof(fna), "digits/million");
2451 } else {
2452 snprintf(fn, sizeof(fn), "digits/1N");
2453 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2455 } else {
2456 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2457 if (res) {
2458 return res;
2460 if (num) {
2461 snprintf(fn, sizeof(fn), "digits/millions");
2462 } else {
2463 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2466 t = 1;
2467 } else if (num < INT_MAX) {
2468 int billions = num / 1000000000;
2469 num = num % 1000000000;
2470 if (billions == 1) {
2471 if (num) {
2472 snprintf(fn, sizeof(fn), "digits/1F");
2473 snprintf(fna, sizeof(fna), "digits/milliard");
2474 } else {
2475 snprintf(fn, sizeof(fn), "digits/1N");
2476 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2478 } else {
2479 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2480 if (res)
2481 return res;
2482 if (num) {
2483 snprintf(fn, sizeof(fna), "digits/milliards");
2484 } else {
2485 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2488 t = 1;
2489 } else if (num == INT_MAX) {
2490 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2491 num = 0;
2492 } else {
2493 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2494 res = -1;
2497 if (!res) {
2498 if (!ast_streamfile(chan, fn, language)) {
2499 if ((audiofd > -1) && (ctrlfd > -1))
2500 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2501 else
2502 res = ast_waitstream(chan, ints);
2504 ast_stopstream(chan);
2505 if (!res) {
2506 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2507 if ((audiofd > -1) && (ctrlfd > -1)) {
2508 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2509 } else {
2510 res = ast_waitstream(chan, ints);
2513 ast_stopstream(chan);
2514 strcpy(fna, "");
2518 return res;
2521 /*! \brief ast_say_enumeration_full_de: German syntax */
2522 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)
2524 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2525 int res = 0, t = 0;
2526 char fn[256] = "", fna[256] = "";
2527 char *gender;
2529 if (options && !strncasecmp(options, "f",1)) {
2530 gender = "F";
2531 } else if (options && !strncasecmp(options, "n",1)) {
2532 gender = "N";
2533 } else {
2534 gender = "";
2537 if (!num)
2538 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2540 while(!res && num) {
2541 if (num < 0) {
2542 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2543 if ( num > INT_MIN ) {
2544 num = -num;
2545 } else {
2546 num = 0;
2548 } else if (num < 100 && t) {
2549 snprintf(fn, sizeof(fn), "digits/and");
2550 t = 0;
2551 } else if (num < 20) {
2552 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2553 num = 0;
2554 } else if (num < 100) {
2555 int ones = num % 10;
2556 if (ones) {
2557 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2558 num -= ones;
2559 } else {
2560 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2561 num = 0;
2563 } else if (num == 100 && t == 0) {
2564 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2565 num = 0;
2566 } else if (num < 1000) {
2567 int hundreds = num / 100;
2568 num = num % 100;
2569 if (hundreds == 1) {
2570 snprintf(fn, sizeof(fn), "digits/1N");
2571 } else {
2572 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2574 if (num) {
2575 snprintf(fna, sizeof(fna), "digits/hundred");
2576 } else {
2577 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2579 t = 1;
2580 } else if (num < 1000000) {
2581 int thousands = num / 1000;
2582 num = num % 1000;
2583 if (thousands == 1) {
2584 if (num) {
2585 snprintf(fn, sizeof(fn), "digits/1N");
2586 snprintf(fna, sizeof(fna), "digits/thousand");
2587 } else {
2588 if (t) {
2589 snprintf(fn, sizeof(fn), "digits/1N");
2590 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2591 } else {
2592 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2595 } else {
2596 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2597 if (res) {
2598 return res;
2600 if (num) {
2601 snprintf(fn, sizeof(fn), "digits/thousand");
2602 } else {
2603 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2606 t = 1;
2607 } else if (num < 1000000000) {
2608 int millions = num / 1000000;
2609 num = num % 1000000;
2610 if (millions == 1) {
2611 if (num) {
2612 snprintf(fn, sizeof(fn), "digits/1F");
2613 snprintf(fna, sizeof(fna), "digits/million");
2614 } else {
2615 snprintf(fn, sizeof(fn), "digits/1N");
2616 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2618 } else {
2619 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2620 if (res) {
2621 return res;
2623 if (num) {
2624 snprintf(fn, sizeof(fn), "digits/millions");
2625 } else {
2626 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2629 t = 1;
2630 } else if (num < INT_MAX) {
2631 int billions = num / 1000000000;
2632 num = num % 1000000000;
2633 if (billions == 1) {
2634 if (num) {
2635 snprintf(fn, sizeof(fn), "digits/1F");
2636 snprintf(fna, sizeof(fna), "digits/milliard");
2637 } else {
2638 snprintf(fn, sizeof(fn), "digits/1N");
2639 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2641 } else {
2642 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2643 if (res)
2644 return res;
2645 if (num) {
2646 snprintf(fn, sizeof(fna), "digits/milliards");
2647 } else {
2648 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2651 t = 1;
2652 } else if (num == INT_MAX) {
2653 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2654 num = 0;
2655 } else {
2656 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2657 res = -1;
2660 if (!res) {
2661 if (!ast_streamfile(chan, fn, language)) {
2662 if ((audiofd > -1) && (ctrlfd > -1))
2663 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2664 else
2665 res = ast_waitstream(chan, ints);
2667 ast_stopstream(chan);
2668 if (!res) {
2669 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2670 if ((audiofd > -1) && (ctrlfd > -1)) {
2671 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2672 } else {
2673 res = ast_waitstream(chan, ints);
2676 ast_stopstream(chan);
2677 strcpy(fna, "");
2681 return res;
2684 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2686 if (!strcasecmp(lang, "en") ) { /* English syntax */
2687 return(ast_say_date_en(chan, t, ints, lang));
2688 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2689 return(ast_say_date_da(chan, t, ints, lang));
2690 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2691 return(ast_say_date_de(chan, t, ints, lang));
2692 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2693 return(ast_say_date_fr(chan, t, ints, lang));
2694 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2695 return(ast_say_date_nl(chan, t, ints, lang));
2696 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2697 return(ast_say_date_pt(chan, t, ints, lang));
2698 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2699 return(ast_say_date_gr(chan, t, ints, lang));
2702 /* Default to English */
2703 return(ast_say_date_en(chan, t, ints, lang));
2706 /* English syntax */
2707 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2709 struct tm tm;
2710 char fn[256];
2711 int res = 0;
2712 ast_localtime(&t,&tm,NULL);
2713 if (!res) {
2714 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2715 res = ast_streamfile(chan, fn, lang);
2716 if (!res)
2717 res = ast_waitstream(chan, ints);
2719 if (!res) {
2720 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2721 res = ast_streamfile(chan, fn, lang);
2722 if (!res)
2723 res = ast_waitstream(chan, ints);
2725 if (!res)
2726 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2727 if (!res)
2728 res = ast_waitstream(chan, ints);
2729 if (!res)
2730 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2731 return res;
2734 /* Danish syntax */
2735 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2737 struct tm tm;
2738 char fn[256];
2739 int res = 0;
2740 ast_localtime(&t,&tm,NULL);
2741 if (!res) {
2742 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2743 res = ast_streamfile(chan, fn, lang);
2744 if (!res)
2745 res = ast_waitstream(chan, ints);
2747 if (!res)
2748 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2749 if (!res)
2750 res = ast_waitstream(chan, ints);
2751 if (!res) {
2752 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2753 res = ast_streamfile(chan, fn, lang);
2754 if (!res)
2755 res = ast_waitstream(chan, ints);
2757 if (!res) {
2758 /* Year */
2759 int year = tm.tm_year + 1900;
2760 if (year > 1999) { /* year 2000 and later */
2761 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2762 } else {
2763 if (year < 1100) {
2764 /* I'm not going to handle 1100 and prior */
2765 /* We'll just be silent on the year, instead of bombing out. */
2766 } else {
2767 /* year 1100 to 1999. will anybody need this?!? */
2768 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2769 res = wait_file(chan, ints, fn, lang);
2770 if (!res) {
2771 res = wait_file(chan,ints, "digits/hundred", lang);
2772 if (!res && year % 100 != 0) {
2773 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2779 return res;
2782 /* German syntax */
2783 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2785 struct tm tm;
2786 char fn[256];
2787 int res = 0;
2788 ast_localtime(&t,&tm,NULL);
2789 if (!res) {
2790 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2791 res = ast_streamfile(chan, fn, lang);
2792 if (!res)
2793 res = ast_waitstream(chan, ints);
2795 if (!res)
2796 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2797 if (!res)
2798 res = ast_waitstream(chan, ints);
2799 if (!res) {
2800 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2801 res = ast_streamfile(chan, fn, lang);
2802 if (!res)
2803 res = ast_waitstream(chan, ints);
2805 if (!res) {
2806 /* Year */
2807 int year = tm.tm_year + 1900;
2808 if (year > 1999) { /* year 2000 and later */
2809 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2810 } else {
2811 if (year < 1100) {
2812 /* I'm not going to handle 1100 and prior */
2813 /* We'll just be silent on the year, instead of bombing out. */
2814 } else {
2815 /* year 1100 to 1999. will anybody need this?!? */
2816 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2817 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2818 res = wait_file(chan, ints, fn, lang);
2819 if (!res) {
2820 res = wait_file(chan,ints, "digits/hundred", lang);
2821 if (!res && year % 100 != 0) {
2822 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2828 return res;
2831 /* French syntax */
2832 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2834 struct tm tm;
2835 char fn[256];
2836 int res = 0;
2837 ast_localtime(&t,&tm,NULL);
2838 if (!res) {
2839 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2840 res = ast_streamfile(chan, fn, lang);
2841 if (!res)
2842 res = ast_waitstream(chan, ints);
2844 if (!res)
2845 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2846 if (!res)
2847 res = ast_waitstream(chan, ints);
2848 if (!res) {
2849 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2850 res = ast_streamfile(chan, fn, lang);
2851 if (!res)
2852 res = ast_waitstream(chan, ints);
2854 if (!res)
2855 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2856 return res;
2859 /* Dutch syntax */
2860 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2862 struct tm tm;
2863 char fn[256];
2864 int res = 0;
2865 ast_localtime(&t,&tm,NULL);
2866 if (!res) {
2867 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2868 res = ast_streamfile(chan, fn, lang);
2869 if (!res)
2870 res = ast_waitstream(chan, ints);
2872 if (!res)
2873 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2874 if (!res) {
2875 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2876 res = ast_streamfile(chan, fn, lang);
2877 if (!res)
2878 res = ast_waitstream(chan, ints);
2880 if (!res)
2881 res = ast_waitstream(chan, ints);
2882 if (!res)
2883 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2884 return res;
2887 /* Portuguese syntax */
2888 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2890 struct tm tm;
2891 char fn[256];
2892 int res = 0;
2893 ast_localtime(&t,&tm,NULL);
2894 localtime_r(&t,&tm);
2895 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2896 if (!res)
2897 res = wait_file(chan, ints, fn, lang);
2898 if (!res)
2899 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2900 if (!res)
2901 res = wait_file(chan, ints, "digits/pt-de", lang);
2902 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2903 if (!res)
2904 res = wait_file(chan, ints, fn, lang);
2905 if (!res)
2906 res = wait_file(chan, ints, "digits/pt-de", lang);
2907 if (!res)
2908 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2910 return res;
2913 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)
2915 if (!strcasecmp(lang, "en") ) { /* English syntax */
2916 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2917 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2918 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
2919 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2920 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
2921 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
2922 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
2923 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2924 return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
2925 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2926 return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
2927 } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
2928 return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
2929 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2930 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
2931 } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
2932 return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
2933 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2934 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
2935 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
2936 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
2937 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2938 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
2941 /* Default to English */
2942 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2945 /* English syntax */
2946 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)
2948 struct tm tm;
2949 int res=0, offset, sndoffset;
2950 char sndfile[256], nextmsg[256];
2952 if (format == NULL)
2953 format = "ABdY 'digits/at' IMp";
2955 ast_localtime(&time,&tm,timezone);
2957 for (offset=0 ; format[offset] != '\0' ; offset++) {
2958 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2959 switch (format[offset]) {
2960 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2961 case '\'':
2962 /* Literal name of a sound file */
2963 sndoffset=0;
2964 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2965 sndfile[sndoffset] = format[offset];
2966 sndfile[sndoffset] = '\0';
2967 res = wait_file(chan,ints,sndfile,lang);
2968 break;
2969 case 'A':
2970 case 'a':
2971 /* Sunday - Saturday */
2972 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2973 res = wait_file(chan,ints,nextmsg,lang);
2974 break;
2975 case 'B':
2976 case 'b':
2977 case 'h':
2978 /* January - December */
2979 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2980 res = wait_file(chan,ints,nextmsg,lang);
2981 break;
2982 case 'm':
2983 /* Month enumerated */
2984 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
2985 break;
2986 case 'd':
2987 case 'e':
2988 /* First - Thirtyfirst */
2989 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
2990 break;
2991 case 'Y':
2992 /* Year */
2993 if (tm.tm_year > 99) {
2994 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2995 } else if (tm.tm_year < 1) {
2996 /* I'm not going to handle 1900 and prior */
2997 /* We'll just be silent on the year, instead of bombing out. */
2998 } else {
2999 res = wait_file(chan, ints, "digits/19", lang);
3000 if (!res) {
3001 if (tm.tm_year <= 9) {
3002 /* 1901 - 1909 */
3003 res = wait_file(chan,ints, "digits/oh", lang);
3006 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3009 break;
3010 case 'I':
3011 case 'l':
3012 /* 12-Hour */
3013 if (tm.tm_hour == 0)
3014 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3015 else if (tm.tm_hour > 12)
3016 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3017 else
3018 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3019 res = wait_file(chan,ints,nextmsg,lang);
3020 break;
3021 case 'H':
3022 case 'k':
3023 /* 24-Hour */
3024 if (format[offset] == 'H') {
3025 /* e.g. oh-eight */
3026 if (tm.tm_hour < 10) {
3027 res = wait_file(chan,ints, "digits/oh",lang);
3029 } else {
3030 /* e.g. eight */
3031 if (tm.tm_hour == 0) {
3032 res = wait_file(chan,ints, "digits/oh",lang);
3035 if (!res) {
3036 if (tm.tm_hour != 0) {
3037 int remainder = tm.tm_hour;
3038 if (tm.tm_hour > 20) {
3039 res = wait_file(chan,ints, "digits/20",lang);
3040 remainder -= 20;
3042 if (!res) {
3043 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3044 res = wait_file(chan,ints,nextmsg,lang);
3048 break;
3049 case 'M':
3050 case 'N':
3051 /* Minute */
3052 if (tm.tm_min == 0) {
3053 if (format[offset] == 'M') {
3054 res = wait_file(chan, ints, "digits/oclock", lang);
3055 } else {
3056 res = wait_file(chan, ints, "digits/hundred", lang);
3058 } else if (tm.tm_min < 10) {
3059 res = wait_file(chan,ints, "digits/oh",lang);
3060 if (!res) {
3061 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3062 res = wait_file(chan,ints,nextmsg,lang);
3064 } else {
3065 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3067 break;
3068 case 'P':
3069 case 'p':
3070 /* AM/PM */
3071 if (tm.tm_hour > 11)
3072 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3073 else
3074 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3075 res = wait_file(chan,ints,nextmsg,lang);
3076 break;
3077 case 'Q':
3078 /* Shorthand for "Today", "Yesterday", or ABdY */
3079 /* XXX As emphasized elsewhere, this should the native way in your
3080 * language to say the date, with changes in what you say, depending
3081 * upon how recent the date is. XXX */
3083 struct timeval now;
3084 struct tm tmnow;
3085 time_t beg_today, tt;
3087 gettimeofday(&now,NULL);
3088 tt = now.tv_sec;
3089 ast_localtime(&tt,&tmnow,timezone);
3090 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3091 /* In any case, it saves not having to do ast_mktime() */
3092 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3093 if (beg_today < time) {
3094 /* Today */
3095 res = wait_file(chan,ints, "digits/today",lang);
3096 } else if (beg_today - 86400 < time) {
3097 /* Yesterday */
3098 res = wait_file(chan,ints, "digits/yesterday",lang);
3099 } else if (beg_today - 86400 * 6 < time) {
3100 /* Within the last week */
3101 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3102 } else if (beg_today - 2628000 < time) {
3103 /* Less than a month ago - "Sunday, October third" */
3104 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3105 } else if (beg_today - 15768000 < time) {
3106 /* Less than 6 months ago - "August seventh" */
3107 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3108 } else {
3109 /* More than 6 months ago - "April nineteenth two thousand three" */
3110 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3113 break;
3114 case 'q':
3115 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3116 /* XXX As emphasized elsewhere, this should the native way in your
3117 * language to say the date, with changes in what you say, depending
3118 * upon how recent the date is. XXX */
3120 struct timeval now;
3121 struct tm tmnow;
3122 time_t beg_today, tt;
3124 gettimeofday(&now,NULL);
3125 tt = now.tv_sec;
3126 ast_localtime(&tt,&tmnow,timezone);
3127 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3128 /* In any case, it saves not having to do ast_mktime() */
3129 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3130 if (beg_today < time) {
3131 /* Today */
3132 } else if ((beg_today - 86400) < time) {
3133 /* Yesterday */
3134 res = wait_file(chan,ints, "digits/yesterday",lang);
3135 } else if (beg_today - 86400 * 6 < time) {
3136 /* Within the last week */
3137 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3138 } else if (beg_today - 2628000 < time) {
3139 /* Less than a month ago - "Sunday, October third" */
3140 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3141 } else if (beg_today - 15768000 < time) {
3142 /* Less than 6 months ago - "August seventh" */
3143 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3144 } else {
3145 /* More than 6 months ago - "April nineteenth two thousand three" */
3146 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3149 break;
3150 case 'R':
3151 res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
3152 break;
3153 case 'S':
3154 /* Seconds */
3155 if (tm.tm_sec == 0) {
3156 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3157 res = wait_file(chan,ints,nextmsg,lang);
3158 } else if (tm.tm_sec < 10) {
3159 res = wait_file(chan,ints, "digits/oh",lang);
3160 if (!res) {
3161 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3162 res = wait_file(chan,ints,nextmsg,lang);
3164 } else {
3165 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
3167 break;
3168 case 'T':
3169 res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
3170 break;
3171 case ' ':
3172 case ' ':
3173 /* Just ignore spaces and tabs */
3174 break;
3175 default:
3176 /* Unknown character */
3177 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3179 /* Jump out on DTMF */
3180 if (res) {
3181 break;
3184 return res;
3187 /* Danish syntax */
3188 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)
3190 struct tm tm;
3191 int res=0, offset, sndoffset;
3192 char sndfile[256], nextmsg[256];
3194 if (!format)
3195 format = "A dBY HMS";
3197 ast_localtime(&time,&tm,timezone);
3199 for (offset=0 ; format[offset] != '\0' ; offset++) {
3200 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3201 switch (format[offset]) {
3202 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3203 case '\'':
3204 /* Literal name of a sound file */
3205 sndoffset=0;
3206 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3207 sndfile[sndoffset] = format[offset];
3208 sndfile[sndoffset] = '\0';
3209 res = wait_file(chan,ints,sndfile,lang);
3210 break;
3211 case 'A':
3212 case 'a':
3213 /* Sunday - Saturday */
3214 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3215 res = wait_file(chan,ints,nextmsg,lang);
3216 break;
3217 case 'B':
3218 case 'b':
3219 case 'h':
3220 /* January - December */
3221 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3222 res = wait_file(chan,ints,nextmsg,lang);
3223 break;
3224 case 'm':
3225 /* Month enumerated */
3226 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3227 break;
3228 case 'd':
3229 case 'e':
3230 /* First - Thirtyfirst */
3231 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3232 break;
3233 case 'Y':
3234 /* Year */
3236 int year = tm.tm_year + 1900;
3237 if (year > 1999) { /* year 2000 and later */
3238 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3239 } else {
3240 if (year < 1100) {
3241 /* I'm not going to handle 1100 and prior */
3242 /* We'll just be silent on the year, instead of bombing out. */
3243 } else {
3244 /* year 1100 to 1999. will anybody need this?!? */
3245 /* say 1967 as 'nineteen hundred seven and sixty' */
3246 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3247 res = wait_file(chan,ints,nextmsg,lang);
3248 if (!res) {
3249 res = wait_file(chan,ints, "digits/hundred",lang);
3250 if (!res && year % 100 != 0) {
3251 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3257 break;
3258 case 'I':
3259 case 'l':
3260 /* 12-Hour */
3261 res = wait_file(chan,ints,"digits/oclock",lang);
3262 if (tm.tm_hour == 0)
3263 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3264 else if (tm.tm_hour > 12)
3265 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3266 else
3267 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3268 if (!res) {
3269 res = wait_file(chan,ints,nextmsg,lang);
3271 break;
3272 case 'H':
3273 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3274 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
3275 res = wait_file(chan,ints, "digits/0",lang);
3277 /* FALLTRHU */
3278 case 'k':
3279 /* 24-Hour */
3280 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3281 break;
3282 case 'M':
3283 /* Minute */
3284 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3285 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3287 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3288 if (tm.tm_min == 1) {
3289 res = wait_file(chan,ints,"digits/minute",lang);
3290 } else {
3291 res = wait_file(chan,ints,"digits/minutes",lang);
3294 break;
3295 case 'P':
3296 case 'p':
3297 /* AM/PM */
3298 if (tm.tm_hour > 11)
3299 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3300 else
3301 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3302 res = wait_file(chan,ints,nextmsg,lang);
3303 break;
3304 case 'Q':
3305 /* Shorthand for "Today", "Yesterday", or AdBY */
3306 /* XXX As emphasized elsewhere, this should the native way in your
3307 * language to say the date, with changes in what you say, depending
3308 * upon how recent the date is. XXX */
3310 struct timeval now;
3311 struct tm tmnow;
3312 time_t beg_today, tt;
3314 gettimeofday(&now,NULL);
3315 tt = now.tv_sec;
3316 ast_localtime(&tt,&tmnow,timezone);
3317 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3318 /* In any case, it saves not having to do ast_mktime() */
3319 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3320 if (beg_today < time) {
3321 /* Today */
3322 res = wait_file(chan,ints, "digits/today",lang);
3323 } else if (beg_today - 86400 < time) {
3324 /* Yesterday */
3325 res = wait_file(chan,ints, "digits/yesterday",lang);
3326 } else {
3327 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3330 break;
3331 case 'q':
3332 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3333 /* XXX As emphasized elsewhere, this should the native way in your
3334 * language to say the date, with changes in what you say, depending
3335 * upon how recent the date is. XXX */
3337 struct timeval now;
3338 struct tm tmnow;
3339 time_t beg_today, tt;
3341 gettimeofday(&now,NULL);
3342 tt = now.tv_sec;
3343 ast_localtime(&tt,&tmnow,timezone);
3344 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3345 /* In any case, it saves not having to do ast_mktime() */
3346 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3347 if (beg_today < time) {
3348 /* Today */
3349 } else if ((beg_today - 86400) < time) {
3350 /* Yesterday */
3351 res = wait_file(chan,ints, "digits/yesterday",lang);
3352 } else if (beg_today - 86400 * 6 < time) {
3353 /* Within the last week */
3354 res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
3355 } else {
3356 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3359 break;
3360 case 'R':
3361 res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
3362 break;
3363 case 'S':
3364 /* Seconds */
3365 res = wait_file(chan,ints, "digits/and",lang);
3366 if (!res) {
3367 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3368 if (!res) {
3369 res = wait_file(chan,ints, "digits/seconds",lang);
3372 break;
3373 case 'T':
3374 res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
3375 break;
3376 case ' ':
3377 case ' ':
3378 /* Just ignore spaces and tabs */
3379 break;
3380 default:
3381 /* Unknown character */
3382 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3384 /* Jump out on DTMF */
3385 if (res) {
3386 break;
3389 return res;
3392 /* German syntax */
3393 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)
3395 struct tm tm;
3396 int res=0, offset, sndoffset;
3397 char sndfile[256], nextmsg[256];
3399 if (!format)
3400 format = "A dBY HMS";
3402 ast_localtime(&time,&tm,timezone);
3404 for (offset=0 ; format[offset] != '\0' ; offset++) {
3405 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3406 switch (format[offset]) {
3407 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3408 case '\'':
3409 /* Literal name of a sound file */
3410 sndoffset=0;
3411 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3412 sndfile[sndoffset] = format[offset];
3413 sndfile[sndoffset] = '\0';
3414 res = wait_file(chan,ints,sndfile,lang);
3415 break;
3416 case 'A':
3417 case 'a':
3418 /* Sunday - Saturday */
3419 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3420 res = wait_file(chan,ints,nextmsg,lang);
3421 break;
3422 case 'B':
3423 case 'b':
3424 case 'h':
3425 /* January - December */
3426 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3427 res = wait_file(chan,ints,nextmsg,lang);
3428 break;
3429 case 'm':
3430 /* Month enumerated */
3431 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3432 break;
3433 case 'd':
3434 case 'e':
3435 /* First - Thirtyfirst */
3436 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3437 break;
3438 case 'Y':
3439 /* Year */
3441 int year = tm.tm_year + 1900;
3442 if (year > 1999) { /* year 2000 and later */
3443 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3444 } else {
3445 if (year < 1100) {
3446 /* I'm not going to handle 1100 and prior */
3447 /* We'll just be silent on the year, instead of bombing out. */
3448 } else {
3449 /* year 1100 to 1999. will anybody need this?!? */
3450 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3451 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3452 res = wait_file(chan,ints,nextmsg,lang);
3453 if (!res) {
3454 res = wait_file(chan,ints, "digits/hundred",lang);
3455 if (!res && year % 100 != 0) {
3456 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3462 break;
3463 case 'I':
3464 case 'l':
3465 /* 12-Hour */
3466 if (tm.tm_hour == 0)
3467 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3468 else if (tm.tm_hour > 12)
3469 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3470 else
3471 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3472 res = wait_file(chan,ints,nextmsg,lang);
3473 if (!res) {
3474 res = wait_file(chan,ints,"digits/oclock",lang);
3476 break;
3477 case 'H':
3478 case 'k':
3479 /* 24-Hour */
3480 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3481 if (!res) {
3482 res = wait_file(chan,ints,"digits/oclock",lang);
3484 break;
3485 case 'M':
3486 /* Minute */
3487 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3488 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3490 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3491 if (tm.tm_min == 1) {
3492 res = wait_file(chan,ints,"digits/minute",lang);
3493 } else {
3494 res = wait_file(chan,ints,"digits/minutes",lang);
3497 break;
3498 case 'P':
3499 case 'p':
3500 /* AM/PM */
3501 if (tm.tm_hour > 11)
3502 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3503 else
3504 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3505 res = wait_file(chan,ints,nextmsg,lang);
3506 break;
3507 case 'Q':
3508 /* Shorthand for "Today", "Yesterday", or AdBY */
3509 /* XXX As emphasized elsewhere, this should the native way in your
3510 * language to say the date, with changes in what you say, depending
3511 * upon how recent the date is. XXX */
3513 struct timeval now;
3514 struct tm tmnow;
3515 time_t beg_today, tt;
3517 gettimeofday(&now,NULL);
3518 tt = now.tv_sec;
3519 ast_localtime(&tt,&tmnow,timezone);
3520 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3521 /* In any case, it saves not having to do ast_mktime() */
3522 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3523 if (beg_today < time) {
3524 /* Today */
3525 res = wait_file(chan,ints, "digits/today",lang);
3526 } else if (beg_today - 86400 < time) {
3527 /* Yesterday */
3528 res = wait_file(chan,ints, "digits/yesterday",lang);
3529 } else {
3530 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3533 break;
3534 case 'q':
3535 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3536 /* XXX As emphasized elsewhere, this should the native way in your
3537 * language to say the date, with changes in what you say, depending
3538 * upon how recent the date is. XXX */
3540 struct timeval now;
3541 struct tm tmnow;
3542 time_t beg_today, tt;
3544 gettimeofday(&now,NULL);
3545 tt = now.tv_sec;
3546 ast_localtime(&tt,&tmnow,timezone);
3547 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3548 /* In any case, it saves not having to do ast_mktime() */
3549 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3550 if (beg_today < time) {
3551 /* Today */
3552 } else if ((beg_today - 86400) < time) {
3553 /* Yesterday */
3554 res = wait_file(chan,ints, "digits/yesterday",lang);
3555 } else if (beg_today - 86400 * 6 < time) {
3556 /* Within the last week */
3557 res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
3558 } else {
3559 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3562 break;
3563 case 'R':
3564 res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
3565 break;
3566 case 'S':
3567 /* Seconds */
3568 res = wait_file(chan,ints, "digits/and",lang);
3569 if (!res) {
3570 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3571 if (!res) {
3572 res = wait_file(chan,ints, "digits/seconds",lang);
3575 break;
3576 case 'T':
3577 res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
3578 break;
3579 case ' ':
3580 case ' ':
3581 /* Just ignore spaces and tabs */
3582 break;
3583 default:
3584 /* Unknown character */
3585 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3587 /* Jump out on DTMF */
3588 if (res) {
3589 break;
3592 return res;
3595 /* TODO: this probably is not the correct format for doxygen remarks */
3597 /** ast_say_date_with_format_he Say formmated date in Hebrew
3599 * \ref ast_say_date_with_format_en for the details of the options
3601 * Changes from the English version:
3603 * * don't replicate in here the logic of ast_say_number_full_he
3605 * * year is always 4-digit (because it's simpler)
3607 * * added c, x, and X. Mainly for my tests
3609 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3611 * TODO:
3612 * * A "ha" is missing in the standard date format, before the 'd'.
3613 * * The numbers of 3000--19000 are not handled well
3615 #define IL_DATE_STR "AdBY"
3616 #define IL_TIME_STR "IMp"
3617 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3618 int ast_say_date_with_format_he(struct ast_channel *chan, time_t time,
3619 const char *ints, const char *lang, const char *format,
3620 const char *timezone)
3622 /* TODO: This whole function is cut&paste from
3623 * ast_say_date_with_format_en . Is that considered acceptable?
3625 struct tm tm;
3626 int res=0, offset, sndoffset;
3627 char sndfile[256], nextmsg[256];
3629 if (!format)
3630 format = IL_DATE_STR_FULL;
3632 ast_localtime(&time,&tm,timezone);
3634 for (offset=0 ; format[offset] != '\0' ; offset++) {
3635 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3636 switch (format[offset]) {
3637 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3638 case '\'':
3639 /* Literal name of a sound file */
3640 sndoffset=0;
3641 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3642 sndfile[sndoffset] = format[offset];
3643 sndfile[sndoffset] = '\0';
3644 res = wait_file(chan,ints,sndfile,lang);
3645 break;
3646 case 'A':
3647 case 'a':
3648 /* Sunday - Saturday */
3649 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3650 res = wait_file(chan,ints,nextmsg,lang);
3651 break;
3652 case 'B':
3653 case 'b':
3654 case 'h':
3655 /* January - December */
3656 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3657 res = wait_file(chan,ints,nextmsg,lang);
3658 break;
3659 case 'd':
3660 case 'e': /* Day of the month */
3661 /* I'm not sure exactly what the parameters
3662 * audiofd and ctrlfd to
3663 * ast_say_number_full_he mean, but it seems
3664 * safe to pass -1 there.
3666 * At least in one of the pathes :-(
3668 res = ast_say_number_full_he(chan, tm.tm_mday,
3669 ints, lang, "m", -1, -1
3671 break;
3672 case 'Y': /* Year */
3673 res = ast_say_number_full_he(chan, tm.tm_year+1900,
3674 ints, lang, "f", -1, -1
3676 break;
3677 case 'I':
3678 case 'l': /* 12-Hour */
3680 int hour = tm.tm_hour;
3681 hour = hour%12;
3682 if (hour == 0) hour=12;
3684 res = ast_say_number_full_he(chan, hour,
3685 ints, lang, "f", -1, -1
3688 break;
3689 case 'H':
3690 case 'k': /* 24-Hour */
3691 /* With 'H' there is an 'oh' after a single-
3692 * digit hour */
3693 if ((format[offset] == 'H') &&
3694 (tm.tm_hour <10)&&(tm.tm_hour>0)
3695 ) { /* e.g. oh-eight */
3696 res = wait_file(chan,ints, "digits/oh",lang);
3699 res = ast_say_number_full_he(chan, tm.tm_hour,
3700 ints, lang, "f", -1, -1
3702 break;
3703 case 'M': /* Minute */
3704 res = ast_say_number_full_he(chan, tm.tm_min,
3705 ints, lang,"f", -1, -1
3707 break;
3708 case 'P':
3709 case 'p':
3710 /* AM/PM */
3711 if (tm.tm_hour > 11)
3712 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3713 else
3714 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3715 res = wait_file(chan,ints,nextmsg,lang);
3716 break;
3717 case 'Q':
3718 /* Shorthand for "Today", "Yesterday", or "date" */
3719 case 'q':
3720 /* Shorthand for "" (today), "Yesterday", A
3721 * (weekday), or "date" */
3722 /* XXX As emphasized elsewhere, this should the native way in your
3723 * language to say the date, with changes in what you say, depending
3724 * upon how recent the date is. XXX */
3726 struct timeval now;
3727 struct tm tmnow;
3728 time_t beg_today, tt;
3729 char todo = format[offset]; /* The letter to format*/
3731 gettimeofday(&now,NULL);
3732 tt = now.tv_sec;
3733 ast_localtime(&tt,&tmnow,timezone);
3734 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3735 /* In any case, it saves not having to do ast_mktime() */
3736 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3737 if (beg_today < time) {
3738 /* Today */
3739 if (todo == 'Q') {
3740 res = wait_file(chan,
3741 ints,
3742 "digits/today",
3743 lang);
3745 } else if (beg_today - 86400 < time) {
3746 /* Yesterday */
3747 res = wait_file(chan,ints, "digits/yesterday",lang);
3748 } else if ((todo != 'Q') &&
3749 (beg_today - 86400 * 6 < time))
3751 /* Within the last week */
3752 res = ast_say_date_with_format_he(chan,
3753 time, ints, lang,
3754 "A", timezone);
3755 } else {
3756 res = ast_say_date_with_format_he(chan,
3757 time, ints, lang,
3758 IL_DATE_STR, timezone);
3761 break;
3762 case 'R':
3763 res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
3764 break;
3765 case 'S': /* Seconds */
3766 res = ast_say_number_full_he(chan, tm.tm_sec,
3767 ints, lang, "f", -1, -1
3769 break;
3770 case 'T':
3771 res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
3772 break;
3773 /* c, x, and X seem useful for testing. Not sure
3774 * if thiey're good for the general public */
3775 case 'c':
3776 res = ast_say_date_with_format_he(chan, time,
3777 ints, lang, IL_DATE_STR_FULL, timezone);
3778 break;
3779 case 'x':
3780 res = ast_say_date_with_format_he(chan, time,
3781 ints, lang, IL_DATE_STR, timezone);
3782 break;
3783 case 'X': /* Currently not locale-dependent...*/
3784 res = ast_say_date_with_format_he(chan, time,
3785 ints, lang, IL_TIME_STR, timezone);
3786 break;
3787 case ' ':
3788 case ' ':
3789 /* Just ignore spaces and tabs */
3790 break;
3791 default:
3792 /* Unknown character */
3793 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3795 /* Jump out on DTMF */
3796 if (res) {
3797 break;
3800 return res;
3804 /* Spanish syntax */
3805 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)
3807 struct tm tm;
3808 int res=0, offset, sndoffset;
3809 char sndfile[256], nextmsg[256];
3811 if (format == NULL)
3812 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3814 ast_localtime(&time,&tm,timezone);
3816 for (offset=0 ; format[offset] != '\0' ; offset++) {
3817 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3818 switch (format[offset]) {
3819 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3820 case '\'':
3821 /* Literal name of a sound file */
3822 sndoffset=0;
3823 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3824 sndfile[sndoffset] = format[offset];
3825 sndfile[sndoffset] = '\0';
3826 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
3827 res = wait_file(chan,ints,nextmsg,lang);
3828 break;
3829 case 'A':
3830 case 'a':
3831 /* Sunday - Saturday */
3832 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3833 res = wait_file(chan,ints,nextmsg,lang);
3834 break;
3835 case 'B':
3836 case 'b':
3837 case 'h':
3838 /* January - December */
3839 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3840 res = wait_file(chan,ints,nextmsg,lang);
3841 break;
3842 case 'm':
3843 /* First - Twelfth */
3844 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
3845 res = wait_file(chan,ints,nextmsg,lang);
3846 break;
3847 case 'd':
3848 case 'e':
3849 /* First - Thirtyfirst */
3850 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
3851 break;
3852 case 'Y':
3853 /* Year */
3854 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3855 break;
3856 case 'I':
3857 case 'l':
3858 /* 12-Hour */
3859 if (tm.tm_hour == 0)
3860 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3861 else if (tm.tm_hour > 12)
3862 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3863 else
3864 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3865 res = wait_file(chan,ints,nextmsg,lang);
3866 break;
3867 case 'H':
3868 case 'k':
3869 /* 24-Hour */
3870 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
3871 break;
3872 case 'M':
3873 /* Minute */
3874 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3875 break;
3876 case 'P':
3877 case 'p':
3878 /* AM/PM */
3879 if (tm.tm_hour > 18)
3880 res = wait_file(chan, ints, "digits/p-m", lang);
3881 else if (tm.tm_hour > 12)
3882 res = wait_file(chan, ints, "digits/afternoon", lang);
3883 else if (tm.tm_hour)
3884 res = wait_file(chan, ints, "digits/a-m", lang);
3885 break;
3886 case 'Q':
3887 /* Shorthand for "Today", "Yesterday", or ABdY */
3888 /* XXX As emphasized elsewhere, this should the native way in your
3889 * language to say the date, with changes in what you say, depending
3890 * upon how recent the date is. XXX */
3892 struct timeval now;
3893 struct tm tmnow;
3894 time_t beg_today, tt;
3896 gettimeofday(&now,NULL);
3897 tt = now.tv_sec;
3898 ast_localtime(&tt,&tmnow,timezone);
3899 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3900 /* In any case, it saves not having to do ast_mktime() */
3901 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3902 if (beg_today < time) {
3903 /* Today */
3904 res = wait_file(chan,ints, "digits/today",lang);
3905 } else if (beg_today - 86400 < time) {
3906 /* Yesterday */
3907 res = wait_file(chan,ints, "digits/yesterday",lang);
3908 } else {
3909 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3912 break;
3913 case 'q':
3914 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3915 /* XXX As emphasized elsewhere, this should the native way in your
3916 * language to say the date, with changes in what you say, depending
3917 * upon how recent the date is. XXX */
3919 struct timeval now;
3920 struct tm tmnow;
3921 time_t beg_today, tt;
3923 gettimeofday(&now,NULL);
3924 tt = now.tv_sec;
3925 ast_localtime(&tt,&tmnow,timezone);
3926 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3927 /* In any case, it saves not having to do ast_mktime() */
3928 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3929 if (beg_today < time) {
3930 /* Today */
3931 res = wait_file(chan,ints, "digits/today",lang);
3932 } else if ((beg_today - 86400) < time) {
3933 /* Yesterday */
3934 res = wait_file(chan,ints, "digits/yesterday",lang);
3935 } else if (beg_today - 86400 * 6 < time) {
3936 /* Within the last week */
3937 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
3938 } else {
3939 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3942 break;
3943 case 'R':
3944 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
3945 break;
3946 case 'S':
3947 /* Seconds */
3948 if (tm.tm_sec == 0) {
3949 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3950 res = wait_file(chan,ints,nextmsg,lang);
3951 } else if (tm.tm_sec < 10) {
3952 res = wait_file(chan,ints, "digits/oh",lang);
3953 if (!res) {
3954 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3955 res = wait_file(chan,ints,nextmsg,lang);
3957 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
3958 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3959 res = wait_file(chan,ints,nextmsg,lang);
3960 } else {
3961 int ten, one;
3962 ten = (tm.tm_sec / 10) * 10;
3963 one = (tm.tm_sec % 10);
3964 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
3965 res = wait_file(chan,ints,nextmsg,lang);
3966 if (!res) {
3967 /* Fifty, not fifty-zero */
3968 if (one != 0) {
3969 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
3970 res = wait_file(chan,ints,nextmsg,lang);
3974 break;
3975 case 'T':
3976 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
3977 break;
3978 case ' ':
3979 case ' ':
3980 /* Just ignore spaces and tabs */
3981 break;
3982 default:
3983 /* Unknown character */
3984 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3986 /* Jump out on DTMF */
3987 if (res) {
3988 break;
3991 return res;
3994 /* French syntax
3995 oclock = heure
3997 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)
3999 struct tm tm;
4000 int res=0, offset, sndoffset;
4001 char sndfile[256], nextmsg[256];
4003 if (format == NULL)
4004 format = "AdBY 'digits/at' IMp";
4006 ast_localtime(&time,&tm,timezone);
4008 for (offset=0 ; format[offset] != '\0' ; offset++) {
4009 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4010 switch (format[offset]) {
4011 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4012 case '\'':
4013 /* Literal name of a sound file */
4014 sndoffset=0;
4015 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4016 sndfile[sndoffset] = format[offset];
4017 sndfile[sndoffset] = '\0';
4018 res = wait_file(chan,ints,sndfile,lang);
4019 break;
4020 case 'A':
4021 case 'a':
4022 /* Sunday - Saturday */
4023 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4024 res = wait_file(chan,ints,nextmsg,lang);
4025 break;
4026 case 'B':
4027 case 'b':
4028 case 'h':
4029 /* January - December */
4030 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4031 res = wait_file(chan,ints,nextmsg,lang);
4032 break;
4033 case 'm':
4034 /* First - Twelfth */
4035 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4036 res = wait_file(chan,ints,nextmsg,lang);
4037 break;
4038 case 'd':
4039 case 'e':
4040 /* First */
4041 if (tm.tm_mday == 1) {
4042 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4043 res = wait_file(chan,ints,nextmsg,lang);
4044 } else {
4045 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4047 break;
4048 case 'Y':
4049 /* Year */
4050 if (tm.tm_year > 99) {
4051 res = wait_file(chan,ints, "digits/2",lang);
4052 if (!res) {
4053 res = wait_file(chan,ints, "digits/thousand",lang);
4055 if (tm.tm_year > 100) {
4056 if (!res) {
4057 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4060 } else {
4061 if (tm.tm_year < 1) {
4062 /* I'm not going to handle 1900 and prior */
4063 /* We'll just be silent on the year, instead of bombing out. */
4064 } else {
4065 res = wait_file(chan,ints, "digits/thousand",lang);
4066 if (!res) {
4067 wait_file(chan,ints, "digits/9",lang);
4068 wait_file(chan,ints, "digits/hundred",lang);
4069 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4073 break;
4074 case 'I':
4075 case 'l':
4076 /* 12-Hour */
4077 if (tm.tm_hour == 0)
4078 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4079 else if (tm.tm_hour > 12)
4080 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4081 else
4082 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4083 res = wait_file(chan,ints,nextmsg,lang);
4084 if (!res)
4085 res = wait_file(chan,ints, "digits/oclock",lang);
4086 break;
4087 case 'H':
4088 case 'k':
4089 /* 24-Hour */
4090 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4091 if (!res)
4092 res = wait_file(chan,ints, "digits/oclock",lang);
4093 break;
4094 case 'M':
4095 /* Minute */
4096 if (tm.tm_min == 0) {
4097 break;
4099 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4100 break;
4101 case 'P':
4102 case 'p':
4103 /* AM/PM */
4104 if (tm.tm_hour > 11)
4105 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4106 else
4107 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4108 res = wait_file(chan,ints,nextmsg,lang);
4109 break;
4110 case 'Q':
4111 /* Shorthand for "Today", "Yesterday", or AdBY */
4112 /* XXX As emphasized elsewhere, this should the native way in your
4113 * language to say the date, with changes in what you say, depending
4114 * upon how recent the date is. XXX */
4116 struct timeval now;
4117 struct tm tmnow;
4118 time_t beg_today, tt;
4120 gettimeofday(&now,NULL);
4121 tt = now.tv_sec;
4122 ast_localtime(&tt,&tmnow,timezone);
4123 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4124 /* In any case, it saves not having to do ast_mktime() */
4125 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4126 if (beg_today < time) {
4127 /* Today */
4128 res = wait_file(chan,ints, "digits/today",lang);
4129 } else if (beg_today - 86400 < time) {
4130 /* Yesterday */
4131 res = wait_file(chan,ints, "digits/yesterday",lang);
4132 } else {
4133 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4136 break;
4137 case 'q':
4138 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4139 /* XXX As emphasized elsewhere, this should the native way in your
4140 * language to say the date, with changes in what you say, depending
4141 * upon how recent the date is. XXX */
4143 struct timeval now;
4144 struct tm tmnow;
4145 time_t beg_today, tt;
4147 gettimeofday(&now,NULL);
4148 tt = now.tv_sec;
4149 ast_localtime(&tt,&tmnow,timezone);
4150 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4151 /* In any case, it saves not having to do ast_mktime() */
4152 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4153 if (beg_today < time) {
4154 /* Today */
4155 } else if ((beg_today - 86400) < time) {
4156 /* Yesterday */
4157 res = wait_file(chan,ints, "digits/yesterday",lang);
4158 } else if (beg_today - 86400 * 6 < time) {
4159 /* Within the last week */
4160 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4161 } else {
4162 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4165 break;
4166 case 'R':
4167 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4168 break;
4169 case 'S':
4170 /* Seconds */
4171 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4172 if (!res) {
4173 res = wait_file(chan,ints, "digits/second",lang);
4175 break;
4176 case 'T':
4177 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4178 break;
4179 case ' ':
4180 case ' ':
4181 /* Just ignore spaces and tabs */
4182 break;
4183 default:
4184 /* Unknown character */
4185 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4187 /* Jump out on DTMF */
4188 if (res) {
4189 break;
4192 return res;
4195 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)
4197 struct tm tm;
4198 int res=0, offset, sndoffset;
4199 char sndfile[256], nextmsg[256];
4201 if (format == NULL)
4202 format = "AdB 'digits/at' IMp";
4204 ast_localtime(&time,&tm,timezone);
4206 for (offset=0 ; format[offset] != '\0' ; offset++) {
4207 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4208 switch (format[offset]) {
4209 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4210 case '\'':
4211 /* Literal name of a sound file */
4212 sndoffset=0;
4213 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4214 sndfile[sndoffset] = format[offset];
4215 sndfile[sndoffset] = '\0';
4216 res = wait_file(chan,ints,sndfile,lang);
4217 break;
4218 case 'A':
4219 case 'a':
4220 /* Sunday - Saturday */
4221 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4222 res = wait_file(chan,ints,nextmsg,lang);
4223 break;
4224 case 'B':
4225 case 'b':
4226 case 'h':
4227 /* January - December */
4228 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4229 res = wait_file(chan,ints,nextmsg,lang);
4230 break;
4231 case 'm':
4232 /* First - Twelfth */
4233 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4234 res = wait_file(chan,ints,nextmsg,lang);
4235 break;
4236 case 'd':
4237 case 'e':
4238 /* First day of the month is spelled as ordinal */
4239 if (tm.tm_mday == 1) {
4240 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4241 res = wait_file(chan,ints,nextmsg,lang);
4242 } else {
4243 if (!res) {
4244 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4247 break;
4248 case 'Y':
4249 /* Year */
4250 if (tm.tm_year > 99) {
4251 res = wait_file(chan,ints, "digits/ore-2000",lang);
4252 if (tm.tm_year > 100) {
4253 if (!res) {
4254 /* This works until the end of 2021 */
4255 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4256 res = wait_file(chan,ints,nextmsg,lang);
4259 } else {
4260 if (tm.tm_year < 1) {
4261 /* I'm not going to handle 1900 and prior */
4262 /* We'll just be silent on the year, instead of bombing out. */
4263 } else {
4264 res = wait_file(chan,ints, "digits/ore-1900",lang);
4265 if ((!res) && (tm.tm_year != 0)) {
4266 if (tm.tm_year <= 21) {
4267 /* 1910 - 1921 */
4268 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4269 res = wait_file(chan,ints,nextmsg,lang);
4270 } else {
4271 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4272 int ten, one;
4273 ten = tm.tm_year / 10;
4274 one = tm.tm_year % 10;
4275 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4276 res = wait_file(chan,ints,nextmsg,lang);
4277 if (!res) {
4278 if (one != 0) {
4279 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4280 res = wait_file(chan,ints,nextmsg,lang);
4287 break;
4288 case 'I':
4289 case 'l':
4290 /* 12-Hour */
4291 if (tm.tm_hour == 0)
4292 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4293 else if (tm.tm_hour > 12)
4294 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4295 else
4296 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4297 res = wait_file(chan,ints,nextmsg,lang);
4298 break;
4299 case 'H':
4300 case 'k':
4301 /* 24-Hour */
4302 if (tm.tm_hour == 0) {
4303 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4304 } else if (tm.tm_hour == 1) {
4305 res = wait_file(chan,ints, "digits/ore-una",lang);
4306 } else {
4307 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4309 break;
4310 case 'M':
4311 /* Minute */
4312 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4313 break;
4314 case 'P':
4315 case 'p':
4316 /* AM/PM */
4317 if (tm.tm_hour > 11)
4318 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4319 else
4320 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4321 res = wait_file(chan,ints,nextmsg,lang);
4322 break;
4323 case 'Q':
4324 /* Shorthand for "Today", "Yesterday", or ABdY */
4325 /* XXX As emphasized elsewhere, this should the native way in your
4326 * language to say the date, with changes in what you say, depending
4327 * upon how recent the date is. XXX */
4329 struct timeval now;
4330 struct tm tmnow;
4331 time_t beg_today, tt;
4333 gettimeofday(&now,NULL);
4334 tt = now.tv_sec;
4335 ast_localtime(&tt,&tmnow,timezone);
4336 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4337 /* In any case, it saves not having to do ast_mktime() */
4338 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4339 if (beg_today < time) {
4340 /* Today */
4341 res = wait_file(chan,ints, "digits/today",lang);
4342 } else if (beg_today - 86400 < time) {
4343 /* Yesterday */
4344 res = wait_file(chan,ints, "digits/yesterday",lang);
4345 } else {
4346 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4349 break;
4350 case 'q':
4351 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4353 struct timeval now;
4354 struct tm tmnow;
4355 time_t beg_today, tt;
4357 gettimeofday(&now,NULL);
4358 tt = now.tv_sec;
4359 ast_localtime(&tt,&tmnow,timezone);
4360 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4361 /* In any case, it saves not having to do ast_mktime() */
4362 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4363 if (beg_today < time) {
4364 /* Today */
4365 } else if ((beg_today - 86400) < time) {
4366 /* Yesterday */
4367 res = wait_file(chan,ints, "digits/yesterday",lang);
4368 } else if (beg_today - 86400 * 6 < time) {
4369 /* Within the last week */
4370 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4371 } else {
4372 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4375 break;
4376 case 'R':
4377 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4378 break;
4379 case 'S':
4380 /* Seconds */
4381 if (tm.tm_sec == 0) {
4382 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4383 res = wait_file(chan,ints,nextmsg,lang);
4384 } else if (tm.tm_sec < 10) {
4385 res = wait_file(chan,ints, "digits/oh",lang);
4386 if (!res) {
4387 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4388 res = wait_file(chan,ints,nextmsg,lang);
4390 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4391 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4392 res = wait_file(chan,ints,nextmsg,lang);
4393 } else {
4394 int ten, one;
4395 ten = (tm.tm_sec / 10) * 10;
4396 one = (tm.tm_sec % 10);
4397 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4398 res = wait_file(chan,ints,nextmsg,lang);
4399 if (!res) {
4400 /* Fifty, not fifty-zero */
4401 if (one != 0) {
4402 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4403 res = wait_file(chan,ints,nextmsg,lang);
4407 break;
4408 case 'T':
4409 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4410 break;
4411 case ' ':
4412 case ' ':
4413 /* Just ignore spaces and tabs */
4414 break;
4415 default:
4416 /* Unknown character */
4417 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4419 /* Jump out on DTMF */
4420 if (res) {
4421 break;
4424 return res;
4427 /* Dutch syntax */
4428 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)
4430 struct tm tm;
4431 int res=0, offset, sndoffset;
4432 char sndfile[256], nextmsg[256];
4434 if (format == NULL)
4435 format = "ABdY 'digits/at' IMp";
4437 ast_localtime(&time,&tm,timezone);
4439 for (offset=0 ; format[offset] != '\0' ; offset++) {
4440 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4441 switch (format[offset]) {
4442 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4443 case '\'':
4444 /* Literal name of a sound file */
4445 sndoffset=0;
4446 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4447 sndfile[sndoffset] = format[offset];
4448 sndfile[sndoffset] = '\0';
4449 res = wait_file(chan,ints,sndfile,lang);
4450 break;
4451 case 'A':
4452 case 'a':
4453 /* Sunday - Saturday */
4454 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4455 res = wait_file(chan,ints,nextmsg,lang);
4456 break;
4457 case 'B':
4458 case 'b':
4459 case 'h':
4460 /* January - December */
4461 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4462 res = wait_file(chan,ints,nextmsg,lang);
4463 break;
4464 case 'm':
4465 /* First - Twelfth */
4466 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4467 res = wait_file(chan,ints,nextmsg,lang);
4468 break;
4469 case 'd':
4470 case 'e':
4471 /* First - Thirtyfirst */
4472 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4473 break;
4474 case 'Y':
4475 /* Year */
4476 if (tm.tm_year > 99) {
4477 res = wait_file(chan,ints, "digits/2",lang);
4478 if (!res) {
4479 res = wait_file(chan,ints, "digits/thousand",lang);
4481 if (tm.tm_year > 100) {
4482 if (!res) {
4483 /* This works until the end of 2020 */
4484 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4485 res = wait_file(chan,ints,nextmsg,lang);
4488 } else {
4489 if (tm.tm_year < 1) {
4490 /* I'm not going to handle 1900 and prior */
4491 /* We'll just be silent on the year, instead of bombing out. */
4492 } else {
4493 res = wait_file(chan,ints, "digits/19",lang);
4494 if (!res) {
4495 if (tm.tm_year <= 9) {
4496 /* 1901 - 1909 */
4497 res = wait_file(chan,ints, "digits/oh",lang);
4498 if (!res) {
4499 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4500 res = wait_file(chan,ints,nextmsg,lang);
4502 } else if (tm.tm_year <= 20) {
4503 /* 1910 - 1920 */
4504 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4505 res = wait_file(chan,ints,nextmsg,lang);
4506 } else {
4507 /* 1921 - 1999 */
4508 int ten, one;
4509 ten = tm.tm_year / 10;
4510 one = tm.tm_year % 10;
4511 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4512 res = wait_file(chan,ints,nextmsg,lang);
4513 if (!res) {
4514 if (one != 0) {
4515 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4516 res = wait_file(chan,ints,nextmsg,lang);
4523 break;
4524 case 'I':
4525 case 'l':
4526 /* 12-Hour */
4527 if (tm.tm_hour == 0)
4528 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4529 else if (tm.tm_hour > 12)
4530 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4531 else
4532 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4533 res = wait_file(chan,ints,nextmsg,lang);
4534 break;
4535 case 'H':
4536 case 'k':
4537 /* 24-Hour */
4538 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4539 if (!res) {
4540 res = wait_file(chan,ints, "digits/nl-uur",lang);
4542 break;
4543 case 'M':
4544 /* Minute */
4545 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4546 break;
4547 case 'P':
4548 case 'p':
4549 /* AM/PM */
4550 if (tm.tm_hour > 11)
4551 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4552 else
4553 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4554 res = wait_file(chan,ints,nextmsg,lang);
4555 break;
4556 case 'Q':
4557 /* Shorthand for "Today", "Yesterday", or ABdY */
4558 /* XXX As emphasized elsewhere, this should the native way in your
4559 * language to say the date, with changes in what you say, depending
4560 * upon how recent the date is. XXX */
4562 struct timeval now;
4563 struct tm tmnow;
4564 time_t beg_today, tt;
4566 gettimeofday(&now,NULL);
4567 tt = now.tv_sec;
4568 ast_localtime(&tt,&tmnow,timezone);
4569 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4570 /* In any case, it saves not having to do ast_mktime() */
4571 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4572 if (beg_today < time) {
4573 /* Today */
4574 res = wait_file(chan,ints, "digits/today",lang);
4575 } else if (beg_today - 86400 < time) {
4576 /* Yesterday */
4577 res = wait_file(chan,ints, "digits/yesterday",lang);
4578 } else {
4579 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4582 break;
4583 case 'q':
4584 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4586 struct timeval now;
4587 struct tm tmnow;
4588 time_t beg_today, tt;
4590 gettimeofday(&now,NULL);
4591 tt = now.tv_sec;
4592 ast_localtime(&tt,&tmnow,timezone);
4593 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4594 /* In any case, it saves not having to do ast_mktime() */
4595 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4596 if (beg_today < time) {
4597 /* Today */
4598 } else if ((beg_today - 86400) < time) {
4599 /* Yesterday */
4600 res = wait_file(chan,ints, "digits/yesterday",lang);
4601 } else if (beg_today - 86400 * 6 < time) {
4602 /* Within the last week */
4603 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4604 } else {
4605 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4608 break;
4609 case 'R':
4610 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4611 break;
4612 case 'S':
4613 /* Seconds */
4614 if (tm.tm_sec == 0) {
4615 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4616 res = wait_file(chan,ints,nextmsg,lang);
4617 } else if (tm.tm_sec < 10) {
4618 res = wait_file(chan,ints, "digits/oh",lang);
4619 if (!res) {
4620 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4621 res = wait_file(chan,ints,nextmsg,lang);
4623 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4624 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4625 res = wait_file(chan,ints,nextmsg,lang);
4626 } else {
4627 int ten, one;
4628 ten = (tm.tm_sec / 10) * 10;
4629 one = (tm.tm_sec % 10);
4630 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4631 res = wait_file(chan,ints,nextmsg,lang);
4632 if (!res) {
4633 /* Fifty, not fifty-zero */
4634 if (one != 0) {
4635 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4636 res = wait_file(chan,ints,nextmsg,lang);
4640 break;
4641 case 'T':
4642 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4643 break;
4644 case ' ':
4645 case ' ':
4646 /* Just ignore spaces and tabs */
4647 break;
4648 default:
4649 /* Unknown character */
4650 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4652 /* Jump out on DTMF */
4653 if (res) {
4654 break;
4657 return res;
4660 /* Polish syntax */
4661 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)
4663 struct tm tm;
4664 int res=0, offset, sndoffset;
4665 char sndfile[256], nextmsg[256];
4667 ast_localtime(&thetime, &tm, timezone);
4669 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4670 int remainder;
4671 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4672 switch (format[offset]) {
4673 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4674 case '\'':
4675 /* Literal name of a sound file */
4676 sndoffset = 0;
4677 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4678 sndfile[sndoffset] = format[offset];
4679 sndfile[sndoffset] = '\0';
4680 res = wait_file(chan, ints, sndfile, lang);
4681 break;
4682 case 'A':
4683 case 'a':
4684 /* Sunday - Saturday */
4685 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4686 res = wait_file(chan, ints, nextmsg, lang);
4687 break;
4688 case 'B':
4689 case 'b':
4690 case 'h':
4691 /* January - December */
4692 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4693 res = wait_file(chan, ints, nextmsg, lang);
4694 break;
4695 case 'm':
4696 /* Month enumerated */
4697 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4698 break;
4699 case 'd':
4700 case 'e':
4701 /* First - Thirtyfirst */
4702 remainder = tm.tm_mday;
4703 if (tm.tm_mday > 30) {
4704 res = wait_file(chan, ints, "digits/h-30", lang);
4705 remainder -= 30;
4707 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
4708 res = wait_file(chan, ints, "digits/h-20", lang);
4709 remainder -= 20;
4711 if (!res) {
4712 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4713 res = wait_file(chan, ints, nextmsg, lang);
4715 break;
4716 case 'Y':
4717 /* Year */
4718 if (tm.tm_year > 100) {
4719 res = wait_file(chan, ints, "digits/2", lang);
4720 if (!res)
4721 res = wait_file(chan, ints, "digits/1000.2",lang);
4722 if (tm.tm_year > 100) {
4723 if (!res)
4724 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4726 } else if (tm.tm_year == 100) {
4727 res = wait_file(chan, ints, "digits/h-2000", lang);
4728 } else {
4729 if (tm.tm_year < 1) {
4730 /* I'm not going to handle 1900 and prior */
4731 /* We'll just be silent on the year, instead of bombing out. */
4732 break;
4733 } else {
4734 res = wait_file(chan, ints, "digits/1000", lang);
4735 if (!res) {
4736 wait_file(chan, ints, "digits/900", lang);
4737 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4741 if (!res)
4742 wait_file(chan, ints, "digits/year", lang);
4743 break;
4744 case 'I':
4745 case 'l':
4746 /* 12-Hour */
4747 if (tm.tm_hour == 0)
4748 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4749 else if (tm.tm_hour > 12)
4750 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4751 else
4752 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4754 res = wait_file(chan, ints, nextmsg, lang);
4755 break;
4756 case 'H':
4757 case 'k':
4758 /* 24-Hour */
4759 if (tm.tm_hour != 0) {
4760 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4761 res = wait_file(chan, ints, nextmsg, lang);
4762 } else
4763 res = wait_file(chan, ints, "digits/t-24", lang);
4764 break;
4765 case 'M':
4766 case 'N':
4767 /* Minute */
4768 if (tm.tm_min == 0) {
4769 if (format[offset] == 'M') {
4770 res = wait_file(chan, ints, "digits/oclock", lang);
4771 } else {
4772 res = wait_file(chan, ints, "digits/100", lang);
4774 } else
4775 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4776 break;
4777 case 'P':
4778 case 'p':
4779 /* AM/PM */
4780 if (tm.tm_hour > 11)
4781 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4782 else
4783 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4784 res = wait_file(chan, ints, nextmsg, lang);
4785 break;
4786 case 'Q':
4787 /* Shorthand for "Today", "Yesterday", or AdBY */
4789 time_t tv_sec = time(NULL);
4790 struct tm tmnow;
4791 time_t beg_today;
4793 ast_localtime(&tv_sec,&tmnow, timezone);
4794 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4795 /* In any case, it saves not having to do ast_mktime() */
4796 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4797 if (beg_today < thetime) {
4798 /* Today */
4799 res = wait_file(chan, ints, "digits/today", lang);
4800 } else if (beg_today - 86400 < thetime) {
4801 /* Yesterday */
4802 res = wait_file(chan, ints, "digits/yesterday", lang);
4803 } else {
4804 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4807 break;
4808 case 'q':
4809 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4811 time_t tv_sec = time(NULL);
4812 struct tm tmnow;
4813 time_t beg_today;
4815 ast_localtime(&tv_sec, &tmnow, timezone);
4816 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4817 /* In any case, it saves not having to do ast_mktime() */
4818 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4819 if (beg_today < thetime) {
4820 /* Today */
4821 } else if ((beg_today - 86400) < thetime) {
4822 /* Yesterday */
4823 res = wait_file(chan, ints, "digits/yesterday", lang);
4824 } else if (beg_today - 86400 * 6 < thetime) {
4825 /* Within the last week */
4826 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
4827 } else {
4828 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4831 break;
4832 case 'R':
4833 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
4834 break;
4835 case 'S':
4836 /* Seconds */
4837 res = wait_file(chan, ints, "digits/and", lang);
4838 if (!res) {
4839 if (tm.tm_sec == 1) {
4840 res = wait_file(chan, ints, "digits/1z", lang);
4841 if (!res)
4842 res = wait_file(chan, ints, "digits/second-a", lang);
4843 } else {
4844 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4845 if (!res) {
4846 int ten, one;
4847 ten = tm.tm_sec / 10;
4848 one = tm.tm_sec % 10;
4850 if (one > 1 && one < 5 && ten != 1)
4851 res = wait_file(chan,ints, "digits/seconds",lang);
4852 else
4853 res = wait_file(chan,ints, "digits/second",lang);
4857 break;
4858 case 'T':
4859 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
4860 break;
4861 case ' ':
4862 case ' ':
4863 /* Just ignore spaces and tabs */
4864 break;
4865 default:
4866 /* Unknown character */
4867 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4869 /* Jump out on DTMF */
4870 if (res)
4871 break;
4873 return res;
4876 /* Portuguese syntax */
4877 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)
4879 struct tm tm;
4880 int res=0, offset, sndoffset;
4881 char sndfile[256], nextmsg[256];
4883 if (format == NULL)
4884 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4886 ast_localtime(&time,&tm,timezone);
4888 for (offset=0 ; format[offset] != '\0' ; offset++) {
4889 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4890 switch (format[offset]) {
4891 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4892 case '\'':
4893 /* Literal name of a sound file */
4894 sndoffset=0;
4895 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4896 sndfile[sndoffset] = format[offset];
4897 sndfile[sndoffset] = '\0';
4898 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4899 res = wait_file(chan,ints,nextmsg,lang);
4900 break;
4901 case 'A':
4902 case 'a':
4903 /* Sunday - Saturday */
4904 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4905 res = wait_file(chan,ints,nextmsg,lang);
4906 break;
4907 case 'B':
4908 case 'b':
4909 case 'h':
4910 /* January - December */
4911 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4912 res = wait_file(chan,ints,nextmsg,lang);
4913 break;
4914 case 'm':
4915 /* First - Twelfth */
4916 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4917 res = wait_file(chan,ints,nextmsg,lang);
4918 break;
4919 case 'd':
4920 case 'e':
4921 /* First - Thirtyfirst */
4922 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4923 break;
4924 case 'Y':
4925 /* Year */
4926 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4927 break;
4928 case 'I':
4929 case 'l':
4930 /* 12-Hour */
4931 if (tm.tm_hour == 0) {
4932 if (format[offset] == 'I')
4933 res = wait_file(chan, ints, "digits/pt-ah", lang);
4934 if (!res)
4935 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4937 else if (tm.tm_hour == 12) {
4938 if (format[offset] == 'I')
4939 res = wait_file(chan, ints, "digits/pt-ao", lang);
4940 if (!res)
4941 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4943 else {
4944 if (format[offset] == 'I') {
4945 res = wait_file(chan, ints, "digits/pt-ah", lang);
4946 if ((tm.tm_hour % 12) != 1)
4947 if (!res)
4948 res = wait_file(chan, ints, "digits/pt-sss", lang);
4950 if (!res)
4951 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
4953 break;
4954 case 'H':
4955 case 'k':
4956 /* 24-Hour */
4957 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
4958 if (!res) {
4959 if (tm.tm_hour != 0) {
4960 int remainder = tm.tm_hour;
4961 if (tm.tm_hour > 20) {
4962 res = wait_file(chan,ints, "digits/20",lang);
4963 remainder -= 20;
4965 if (!res) {
4966 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
4967 res = wait_file(chan,ints,nextmsg,lang);
4971 break;
4972 case 'M':
4973 /* Minute */
4974 if (tm.tm_min == 0) {
4975 res = wait_file(chan, ints, "digits/pt-hora", lang);
4976 if (tm.tm_hour != 1)
4977 if (!res)
4978 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
4979 res = wait_file(chan,ints,"digits/pt-e",lang);
4980 if (!res)
4981 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4983 break;
4984 case 'P':
4985 case 'p':
4986 /* AM/PM */
4987 if (tm.tm_hour > 12)
4988 res = wait_file(chan, ints, "digits/p-m", lang);
4989 else if (tm.tm_hour && tm.tm_hour < 12)
4990 res = wait_file(chan, ints, "digits/a-m", lang);
4991 break;
4992 case 'Q':
4993 /* Shorthand for "Today", "Yesterday", or ABdY */
4994 /* XXX As emphasized elsewhere, this should the native way in your
4995 * language to say the date, with changes in what you say, depending
4996 * upon how recent the date is. XXX */
4998 struct timeval now;
4999 struct tm tmnow;
5000 time_t beg_today, tt;
5002 gettimeofday(&now,NULL);
5003 tt = now.tv_sec;
5004 ast_localtime(&tt,&tmnow,timezone);
5005 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5006 /* In any case, it saves not having to do ast_mktime() */
5007 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5008 if (beg_today < time) {
5009 /* Today */
5010 res = wait_file(chan,ints, "digits/today",lang);
5011 } else if (beg_today - 86400 < time) {
5012 /* Yesterday */
5013 res = wait_file(chan,ints, "digits/yesterday",lang);
5014 } else {
5015 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5018 break;
5019 case 'q':
5020 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5021 /* XXX As emphasized elsewhere, this should the native way in your
5022 * language to say the date, with changes in what you say, depending
5023 * upon how recent the date is. XXX */
5025 struct timeval now;
5026 struct tm tmnow;
5027 time_t beg_today, tt;
5029 gettimeofday(&now,NULL);
5030 tt = now.tv_sec;
5031 ast_localtime(&tt,&tmnow,timezone);
5032 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5033 /* In any case, it saves not having to do ast_mktime() */
5034 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5035 if (beg_today < time) {
5036 /* Today */
5037 } else if ((beg_today - 86400) < time) {
5038 /* Yesterday */
5039 res = wait_file(chan,ints, "digits/yesterday",lang);
5040 } else if (beg_today - 86400 * 6 < time) {
5041 /* Within the last week */
5042 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5043 } else {
5044 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5047 break;
5048 case 'R':
5049 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5050 break;
5051 case 'S':
5052 /* Seconds */
5053 if (tm.tm_sec == 0) {
5054 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5055 res = wait_file(chan,ints,nextmsg,lang);
5056 } else if (tm.tm_sec < 10) {
5057 res = wait_file(chan,ints, "digits/oh",lang);
5058 if (!res) {
5059 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5060 res = wait_file(chan,ints,nextmsg,lang);
5062 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5063 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5064 res = wait_file(chan,ints,nextmsg,lang);
5065 } else {
5066 int ten, one;
5067 ten = (tm.tm_sec / 10) * 10;
5068 one = (tm.tm_sec % 10);
5069 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5070 res = wait_file(chan,ints,nextmsg,lang);
5071 if (!res) {
5072 /* Fifty, not fifty-zero */
5073 if (one != 0) {
5074 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5075 res = wait_file(chan,ints,nextmsg,lang);
5079 break;
5080 case 'T':
5081 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5082 break;
5083 case ' ':
5084 case ' ':
5085 /* Just ignore spaces and tabs */
5086 break;
5087 default:
5088 /* Unknown character */
5089 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5091 /* Jump out on DTMF */
5092 if (res) {
5093 break;
5096 return res;
5099 /* Taiwanese / Chinese syntax */
5100 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)
5102 struct tm tm;
5103 int res=0, offset, sndoffset;
5104 char sndfile[256], nextmsg[256];
5106 if (format == NULL)
5107 format = "YBdA 'digits/at' HM";
5109 ast_localtime(&time,&tm,timezone);
5111 for (offset=0 ; format[offset] != '\0' ; offset++) {
5112 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5113 switch (format[offset]) {
5114 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5115 case '\'':
5116 /* Literal name of a sound file */
5117 sndoffset=0;
5118 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5119 sndfile[sndoffset] = format[offset];
5120 sndfile[sndoffset] = '\0';
5121 res = wait_file(chan,ints,sndfile,lang);
5122 break;
5123 case 'A':
5124 case 'a':
5125 /* Sunday - Saturday */
5126 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5127 res = wait_file(chan,ints,nextmsg,lang);
5128 break;
5129 case 'B':
5130 case 'b':
5131 case 'h':
5132 /* January - December */
5133 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5134 res = wait_file(chan,ints,nextmsg,lang);
5135 break;
5136 case 'm':
5137 /* First - Twelfth */
5138 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5139 res = wait_file(chan,ints,nextmsg,lang);
5140 break;
5141 case 'd':
5142 case 'e':
5143 /* First - Thirtyfirst */
5144 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5145 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5146 res = wait_file(chan,ints,nextmsg,lang);
5147 } else {
5148 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%dh", tm.tm_mday - (tm.tm_mday % 10));
5149 res = wait_file(chan,ints,nextmsg,lang);
5150 if(!res) {
5151 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday % 10);
5152 res = wait_file(chan,ints,nextmsg,lang);
5155 break;
5156 case 'Y':
5157 /* Year */
5158 if (tm.tm_year > 99) {
5159 res = wait_file(chan,ints, "digits/2",lang);
5160 if (!res) {
5161 res = wait_file(chan,ints, "digits/thousand",lang);
5163 if (tm.tm_year > 100) {
5164 if (!res) {
5165 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5166 res = wait_file(chan,ints,nextmsg,lang);
5167 if (!res) {
5168 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5169 res = wait_file(chan,ints,nextmsg,lang);
5173 if (!res) {
5174 res = wait_file(chan,ints, "digits/year",lang);
5176 } else {
5177 if (tm.tm_year < 1) {
5178 /* I'm not going to handle 1900 and prior */
5179 /* We'll just be silent on the year, instead of bombing out. */
5180 } else {
5181 res = wait_file(chan,ints, "digits/1",lang);
5182 if (!res) {
5183 res = wait_file(chan,ints, "digits/9",lang);
5185 if (!res) {
5186 if (tm.tm_year <= 9) {
5187 /* 1901 - 1909 */
5188 res = wait_file(chan,ints, "digits/0",lang);
5189 if (!res) {
5190 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5191 res = wait_file(chan,ints,nextmsg,lang);
5193 } else {
5194 /* 1910 - 1999 */
5195 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5196 res = wait_file(chan,ints,nextmsg,lang);
5197 if (!res) {
5198 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5199 res = wait_file(chan,ints,nextmsg,lang);
5204 if (!res) {
5205 res = wait_file(chan,ints, "digits/year",lang);
5208 break;
5209 case 'I':
5210 case 'l':
5211 /* 12-Hour */
5212 if (tm.tm_hour == 0)
5213 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5214 else if (tm.tm_hour > 12)
5215 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5216 else
5217 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5218 res = wait_file(chan,ints,nextmsg,lang);
5219 if (!res) {
5220 res = wait_file(chan,ints, "digits/oclock",lang);
5222 break;
5223 case 'H':
5224 case 'k':
5225 /* 24-Hour */
5226 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5227 if (tm.tm_hour < 10) {
5228 res = wait_file(chan, ints, "digits/0", lang);
5230 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5231 res = wait_file(chan,ints,nextmsg,lang);
5232 } else {
5233 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5234 res = wait_file(chan,ints,nextmsg,lang);
5235 if (!res) {
5236 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5237 res = wait_file(chan,ints,nextmsg,lang);
5240 if (!res) {
5241 res = wait_file(chan,ints, "digits/oclock",lang);
5243 break;
5244 case 'M':
5245 /* Minute */
5246 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5247 if (tm.tm_min < 10) {
5248 res = wait_file(chan, ints, "digits/0", lang);
5250 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5251 res = wait_file(chan,ints,nextmsg,lang);
5252 } else {
5253 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5254 res = wait_file(chan,ints,nextmsg,lang);
5255 if (!res) {
5256 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5257 res = wait_file(chan,ints,nextmsg,lang);
5260 if (!res) {
5261 res = wait_file(chan,ints, "digits/minute",lang);
5263 break;
5264 case 'P':
5265 case 'p':
5266 /* AM/PM */
5267 if (tm.tm_hour > 11)
5268 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5269 else
5270 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5271 res = wait_file(chan,ints,nextmsg,lang);
5272 break;
5273 case 'Q':
5274 /* Shorthand for "Today", "Yesterday", or ABdY */
5275 /* XXX As emphasized elsewhere, this should the native way in your
5276 * language to say the date, with changes in what you say, depending
5277 * upon how recent the date is. XXX */
5279 struct timeval now;
5280 struct tm tmnow;
5281 time_t beg_today, tt;
5283 gettimeofday(&now,NULL);
5284 tt = now.tv_sec;
5285 ast_localtime(&tt,&tmnow,timezone);
5286 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5287 /* In any case, it saves not having to do ast_mktime() */
5288 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5289 if (beg_today < time) {
5290 /* Today */
5291 res = wait_file(chan,ints, "digits/today",lang);
5292 } else if (beg_today - 86400 < time) {
5293 /* Yesterday */
5294 res = wait_file(chan,ints, "digits/yesterday",lang);
5295 } else {
5296 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5299 break;
5300 case 'q':
5301 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5302 /* XXX As emphasized elsewhere, this should the native way in your
5303 * language to say the date, with changes in what you say, depending
5304 * upon how recent the date is. XXX */
5306 struct timeval now;
5307 struct tm tmnow;
5308 time_t beg_today, tt;
5310 gettimeofday(&now,NULL);
5311 tt = now.tv_sec;
5312 ast_localtime(&tt,&tmnow,timezone);
5313 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5314 /* In any case, it saves not having to do ast_mktime() */
5315 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5316 if (beg_today < time) {
5317 /* Today */
5318 } else if ((beg_today - 86400) < time) {
5319 /* Yesterday */
5320 res = wait_file(chan,ints, "digits/yesterday",lang);
5321 } else if (beg_today - 86400 * 6 < time) {
5322 /* Within the last week */
5323 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5324 } else {
5325 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5328 break;
5329 case 'R':
5330 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HM", timezone);
5331 break;
5332 case 'S':
5333 /* Seconds */
5334 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5335 if (tm.tm_sec < 10) {
5336 res = wait_file(chan, ints, "digits/0", lang);
5338 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5339 res = wait_file(chan,ints,nextmsg,lang);
5340 } else {
5341 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5342 res = wait_file(chan,ints,nextmsg,lang);
5343 if (!res) {
5344 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5345 res = wait_file(chan,ints,nextmsg,lang);
5348 if (!res) {
5349 res = wait_file(chan,ints, "digits/second",lang);
5351 break;
5352 case 'T':
5353 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5354 break;
5355 case ' ':
5356 case ' ':
5357 /* Just ignore spaces and tabs */
5358 break;
5359 default:
5360 /* Unknown character */
5361 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5363 /* Jump out on DTMF */
5364 if (res) {
5365 break;
5368 return res;
5371 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5373 if (!strcasecmp(lang, "en") ) { /* English syntax */
5374 return(ast_say_time_en(chan, t, ints, lang));
5375 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5376 return(ast_say_time_de(chan, t, ints, lang));
5377 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5378 return(ast_say_time_fr(chan, t, ints, lang));
5379 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5380 return(ast_say_time_nl(chan, t, ints, lang));
5381 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5382 return(ast_say_time_pt(chan, t, ints, lang));
5383 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5384 return(ast_say_time_tw(chan, t, ints, lang));
5385 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5386 return(ast_say_time_gr(chan, t, ints, lang));
5389 /* Default to English */
5390 return(ast_say_time_en(chan, t, ints, lang));
5393 /* English syntax */
5394 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5396 struct tm tm;
5397 int res = 0;
5398 int hour, pm=0;
5399 localtime_r(&t,&tm);
5400 hour = tm.tm_hour;
5401 if (!hour)
5402 hour = 12;
5403 else if (hour == 12)
5404 pm = 1;
5405 else if (hour > 12) {
5406 hour -= 12;
5407 pm = 1;
5409 if (!res)
5410 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5412 if (tm.tm_min > 9) {
5413 if (!res)
5414 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5415 } else if (tm.tm_min) {
5416 if (!res)
5417 res = ast_streamfile(chan, "digits/oh", lang);
5418 if (!res)
5419 res = ast_waitstream(chan, ints);
5420 if (!res)
5421 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5422 } else {
5423 if (!res)
5424 res = ast_streamfile(chan, "digits/oclock", lang);
5425 if (!res)
5426 res = ast_waitstream(chan, ints);
5428 if (pm) {
5429 if (!res)
5430 res = ast_streamfile(chan, "digits/p-m", lang);
5431 } else {
5432 if (!res)
5433 res = ast_streamfile(chan, "digits/a-m", lang);
5435 if (!res)
5436 res = ast_waitstream(chan, ints);
5437 return res;
5440 /* German syntax */
5441 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5443 struct tm tm;
5444 int res = 0;
5445 localtime_r(&t,&tm);
5446 if (!res)
5447 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5448 if (!res)
5449 res = ast_streamfile(chan, "digits/oclock", lang);
5450 if (!res)
5451 res = ast_waitstream(chan, ints);
5452 if (!res)
5453 if (tm.tm_min > 0)
5454 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5455 return res;
5458 /* French syntax */
5459 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5461 struct tm tm;
5462 int res = 0;
5463 localtime_r(&t,&tm);
5465 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5466 if (!res)
5467 res = ast_streamfile(chan, "digits/oclock", lang);
5468 if (tm.tm_min) {
5469 if (!res)
5470 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5472 return res;
5475 /* Dutch syntax */
5476 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5478 struct tm tm;
5479 int res = 0;
5480 localtime_r(&t,&tm);
5481 if (!res)
5482 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5483 if (!res)
5484 res = ast_streamfile(chan, "digits/nl-uur", lang);
5485 if (!res)
5486 res = ast_waitstream(chan, ints);
5487 if (!res)
5488 if (tm.tm_min > 0)
5489 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5490 return res;
5493 /* Portuguese syntax */
5494 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5496 struct tm tm;
5497 int res = 0;
5498 int hour;
5499 localtime_r(&t,&tm);
5500 hour = tm.tm_hour;
5501 if (!res)
5502 res = ast_say_number(chan, hour, ints, lang, "f");
5503 if (tm.tm_min) {
5504 if (!res)
5505 res = wait_file(chan, ints, "digits/pt-e", lang);
5506 if (!res)
5507 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5508 } else {
5509 if (!res)
5510 res = wait_file(chan, ints, "digits/pt-hora", lang);
5511 if (tm.tm_hour != 1)
5512 if (!res)
5513 res = wait_file(chan, ints, "digits/pt-sss", lang);
5515 if (!res)
5516 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5517 return res;
5520 /* Taiwanese / Chinese syntax */
5521 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5523 struct tm tm;
5524 int res = 0;
5525 int hour, pm=0;
5526 localtime_r(&t,&tm);
5527 hour = tm.tm_hour;
5528 if (!hour)
5529 hour = 12;
5530 else if (hour == 12)
5531 pm = 1;
5532 else if (hour > 12) {
5533 hour -= 12;
5534 pm = 1;
5536 if (pm) {
5537 if (!res)
5538 res = ast_streamfile(chan, "digits/p-m", lang);
5539 } else {
5540 if (!res)
5541 res = ast_streamfile(chan, "digits/a-m", lang);
5543 if (!res)
5544 res = ast_waitstream(chan, ints);
5545 if (!res)
5546 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5547 if (!res)
5548 res = ast_streamfile(chan, "digits/oclock", lang);
5549 if (!res)
5550 res = ast_waitstream(chan, ints);
5551 if (!res)
5552 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5553 if (!res)
5554 res = ast_streamfile(chan, "digits/minute", lang);
5555 if (!res)
5556 res = ast_waitstream(chan, ints);
5557 return res;
5560 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5562 if (!strcasecmp(lang, "en") ) { /* English syntax */
5563 return(ast_say_datetime_en(chan, t, ints, lang));
5564 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5565 return(ast_say_datetime_de(chan, t, ints, lang));
5566 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5567 return(ast_say_datetime_fr(chan, t, ints, lang));
5568 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5569 return(ast_say_datetime_nl(chan, t, ints, lang));
5570 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5571 return(ast_say_datetime_pt(chan, t, ints, lang));
5572 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5573 return(ast_say_datetime_tw(chan, t, ints, lang));
5574 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5575 return(ast_say_datetime_gr(chan, t, ints, lang));
5578 /* Default to English */
5579 return(ast_say_datetime_en(chan, t, ints, lang));
5582 /* English syntax */
5583 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5585 struct tm tm;
5586 char fn[256];
5587 int res = 0;
5588 int hour, pm=0;
5589 localtime_r(&t,&tm);
5590 if (!res) {
5591 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5592 res = ast_streamfile(chan, fn, lang);
5593 if (!res)
5594 res = ast_waitstream(chan, ints);
5596 if (!res) {
5597 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5598 res = ast_streamfile(chan, fn, lang);
5599 if (!res)
5600 res = ast_waitstream(chan, ints);
5602 if (!res)
5603 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5605 hour = tm.tm_hour;
5606 if (!hour)
5607 hour = 12;
5608 else if (hour == 12)
5609 pm = 1;
5610 else if (hour > 12) {
5611 hour -= 12;
5612 pm = 1;
5614 if (!res)
5615 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5617 if (tm.tm_min > 9) {
5618 if (!res)
5619 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5620 } else if (tm.tm_min) {
5621 if (!res)
5622 res = ast_streamfile(chan, "digits/oh", lang);
5623 if (!res)
5624 res = ast_waitstream(chan, ints);
5625 if (!res)
5626 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5627 } else {
5628 if (!res)
5629 res = ast_streamfile(chan, "digits/oclock", lang);
5630 if (!res)
5631 res = ast_waitstream(chan, ints);
5633 if (pm) {
5634 if (!res)
5635 res = ast_streamfile(chan, "digits/p-m", lang);
5636 } else {
5637 if (!res)
5638 res = ast_streamfile(chan, "digits/a-m", lang);
5640 if (!res)
5641 res = ast_waitstream(chan, ints);
5642 if (!res)
5643 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5644 return res;
5647 /* German syntax */
5648 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5650 struct tm tm;
5651 int res = 0;
5652 localtime_r(&t,&tm);
5653 res = ast_say_date(chan, t, ints, lang);
5654 if (!res)
5655 ast_say_time(chan, t, ints, lang);
5656 return res;
5660 /* French syntax */
5661 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5663 struct tm tm;
5664 char fn[256];
5665 int res = 0;
5666 localtime_r(&t,&tm);
5668 if (!res)
5669 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5671 if (!res) {
5672 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5673 res = ast_streamfile(chan, fn, lang);
5674 if (!res)
5675 res = ast_waitstream(chan, ints);
5677 if (!res) {
5678 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5679 res = ast_streamfile(chan, fn, lang);
5680 if (!res)
5681 res = ast_waitstream(chan, ints);
5684 if (!res)
5685 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5686 if (!res)
5687 res = ast_streamfile(chan, "digits/oclock", lang);
5688 if (tm.tm_min > 0) {
5689 if (!res)
5690 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5692 if (!res)
5693 res = ast_waitstream(chan, ints);
5694 if (!res)
5695 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5696 return res;
5699 /* Dutch syntax */
5700 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5702 struct tm tm;
5703 int res = 0;
5704 localtime_r(&t,&tm);
5705 res = ast_say_date(chan, t, ints, lang);
5706 if (!res) {
5707 res = ast_streamfile(chan, "digits/nl-om", lang);
5708 if (!res)
5709 res = ast_waitstream(chan, ints);
5711 if (!res)
5712 ast_say_time(chan, t, ints, lang);
5713 return res;
5716 /* Portuguese syntax */
5717 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5719 struct tm tm;
5720 char fn[256];
5721 int res = 0;
5722 int hour, pm=0;
5723 localtime_r(&t,&tm);
5724 if (!res) {
5725 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5726 res = ast_streamfile(chan, fn, lang);
5727 if (!res)
5728 res = ast_waitstream(chan, ints);
5730 if (!res) {
5731 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5732 res = ast_streamfile(chan, fn, lang);
5733 if (!res)
5734 res = ast_waitstream(chan, ints);
5736 if (!res)
5737 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5739 hour = tm.tm_hour;
5740 if (!hour)
5741 hour = 12;
5742 else if (hour == 12)
5743 pm = 1;
5744 else if (hour > 12) {
5745 hour -= 12;
5746 pm = 1;
5748 if (!res)
5749 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5751 if (tm.tm_min > 9) {
5752 if (!res)
5753 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5754 } else if (tm.tm_min) {
5755 if (!res)
5756 res = ast_streamfile(chan, "digits/oh", lang);
5757 if (!res)
5758 res = ast_waitstream(chan, ints);
5759 if (!res)
5760 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5761 } else {
5762 if (!res)
5763 res = ast_streamfile(chan, "digits/oclock", lang);
5764 if (!res)
5765 res = ast_waitstream(chan, ints);
5767 if (pm) {
5768 if (!res)
5769 res = ast_streamfile(chan, "digits/p-m", lang);
5770 } else {
5771 if (!res)
5772 res = ast_streamfile(chan, "digits/a-m", lang);
5774 if (!res)
5775 res = ast_waitstream(chan, ints);
5776 if (!res)
5777 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5778 return res;
5781 /* Taiwanese / Chinese syntax */
5782 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5784 struct tm tm;
5785 char fn[256];
5786 int res = 0;
5787 int hour, pm=0;
5788 localtime_r(&t,&tm);
5789 if (!res)
5790 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5791 if (!res) {
5792 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5793 res = ast_streamfile(chan, fn, lang);
5794 if (!res)
5795 res = ast_waitstream(chan, ints);
5797 if (!res)
5798 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5799 if (!res) {
5800 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5801 res = ast_streamfile(chan, fn, lang);
5802 if (!res)
5803 res = ast_waitstream(chan, ints);
5806 hour = tm.tm_hour;
5807 if (!hour)
5808 hour = 12;
5809 else if (hour == 12)
5810 pm = 1;
5811 else if (hour > 12) {
5812 hour -= 12;
5813 pm = 1;
5815 if (pm) {
5816 if (!res)
5817 res = ast_streamfile(chan, "digits/p-m", lang);
5818 } else {
5819 if (!res)
5820 res = ast_streamfile(chan, "digits/a-m", lang);
5822 if (!res)
5823 res = ast_waitstream(chan, ints);
5824 if (!res)
5825 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5826 if (!res)
5827 res = ast_streamfile(chan, "digits/oclock", lang);
5828 if (!res)
5829 res = ast_waitstream(chan, ints);
5830 if (!res)
5831 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5832 if (!res)
5833 res = ast_streamfile(chan, "digits/minute", lang);
5834 if (!res)
5835 res = ast_waitstream(chan, ints);
5836 return res;
5839 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5841 if (!strcasecmp(lang, "en") ) { /* English syntax */
5842 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5843 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5844 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
5845 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5846 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
5849 /* Default to English */
5850 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5853 /* English syntax */
5854 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5856 int res=0;
5857 time_t nowt;
5858 int daydiff;
5859 struct tm tm;
5860 struct tm now;
5861 char fn[256];
5863 time(&nowt);
5865 localtime_r(&t,&tm);
5866 localtime_r(&nowt,&now);
5867 daydiff = now.tm_yday - tm.tm_yday;
5868 if ((daydiff < 0) || (daydiff > 6)) {
5869 /* Day of month and month */
5870 if (!res) {
5871 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5872 res = ast_streamfile(chan, fn, lang);
5873 if (!res)
5874 res = ast_waitstream(chan, ints);
5876 if (!res)
5877 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5879 } else if (daydiff) {
5880 /* Just what day of the week */
5881 if (!res) {
5882 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5883 res = ast_streamfile(chan, fn, lang);
5884 if (!res)
5885 res = ast_waitstream(chan, ints);
5887 } /* Otherwise, it was today */
5888 if (!res)
5889 res = ast_say_time(chan, t, ints, lang);
5890 return res;
5893 /* French syntax */
5894 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5896 int res=0;
5897 time_t nowt;
5898 int daydiff;
5899 struct tm tm;
5900 struct tm now;
5901 char fn[256];
5903 time(&nowt);
5905 localtime_r(&t,&tm);
5906 localtime_r(&nowt,&now);
5907 daydiff = now.tm_yday - tm.tm_yday;
5908 if ((daydiff < 0) || (daydiff > 6)) {
5909 /* Day of month and month */
5910 if (!res) {
5911 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5912 res = ast_streamfile(chan, fn, lang);
5913 if (!res)
5914 res = ast_waitstream(chan, ints);
5916 if (!res)
5917 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5919 } else if (daydiff) {
5920 /* Just what day of the week */
5921 if (!res) {
5922 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5923 res = ast_streamfile(chan, fn, lang);
5924 if (!res)
5925 res = ast_waitstream(chan, ints);
5927 } /* Otherwise, it was today */
5928 if (!res)
5929 res = ast_say_time(chan, t, ints, lang);
5930 return res;
5933 /* Portuguese syntax */
5934 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5936 int res=0;
5937 time_t nowt;
5938 int daydiff;
5939 struct tm tm;
5940 struct tm now;
5941 char fn[256];
5943 time(&nowt);
5945 localtime_r(&t,&tm);
5946 localtime_r(&nowt,&now);
5947 daydiff = now.tm_yday - tm.tm_yday;
5948 if ((daydiff < 0) || (daydiff > 6)) {
5949 /* Day of month and month */
5950 if (!res)
5951 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5952 if (!res)
5953 res = wait_file(chan, ints, "digits/pt-de", lang);
5954 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5955 if (!res)
5956 res = wait_file(chan, ints, fn, lang);
5958 } else if (daydiff) {
5959 /* Just what day of the week */
5960 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5961 if (!res)
5962 res = wait_file(chan, ints, fn, lang);
5963 } /* Otherwise, it was today */
5964 snprintf(fn, sizeof(fn), "digits/pt-ah");
5965 if (!res)
5966 res = wait_file(chan, ints, fn, lang);
5967 if (tm.tm_hour != 1)
5968 if (!res)
5969 res = wait_file(chan, ints, "digits/pt-sss", lang);
5970 if (!res)
5971 res = ast_say_time(chan, t, ints, lang);
5972 return res;
5976 /*********************************** GREEK SUPPORT ***************************************/
5980 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
5982 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
5983 int tmp;
5984 int left;
5985 int res;
5986 char fn[256] = "";
5988 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
5989 if (num < 5) {
5990 snprintf(fn, sizeof(fn), "digits/female-%d", num);
5991 res = wait_file(chan, ints, fn, lang);
5992 } else if (num < 13) {
5993 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
5994 } else if (num <100 ) {
5995 tmp = (num/10) * 10;
5996 left = num - tmp;
5997 snprintf(fn, sizeof(fn), "digits/%d", tmp);
5998 res = ast_streamfile(chan, fn, lang);
5999 if (!res)
6000 res = ast_waitstream(chan, ints);
6001 if (left)
6002 gr_say_number_female(left, chan, ints, lang);
6004 } else {
6005 return -1;
6007 return res;
6013 * A list of the files that you need to create
6014 -> digits/xilia = "xilia"
6015 -> digits/myrio = "ekatomyrio"
6016 -> digits/thousands = "xiliades"
6017 -> digits/millions = "ektatomyria"
6018 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6019 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6020 e,g 80 = "ogdonta"
6021 Here we must note that we use digits/tens/100 to utter "ekato"
6022 and digits/hundred-100 to utter "ekaton"
6023 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6024 "terakosia". Here again we use hundreds/1000 for "xilia"
6025 and digits/thousnds for "xiliades"
6028 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6030 int res = 0;
6031 char fn[256] = "";
6032 int i=0;
6035 if (!num) {
6036 snprintf(fn, sizeof(fn), "digits/0");
6037 res = ast_streamfile(chan, fn, chan->language);
6038 if (!res)
6039 return ast_waitstream(chan, ints);
6042 while(!res && num ) {
6043 i++;
6044 if (num < 13) {
6045 snprintf(fn, sizeof(fn), "digits/%d", num);
6046 num = 0;
6047 } else if (num <= 100) {
6048 /* 13 < num <= 100 */
6049 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6050 num -= ((num / 10) * 10);
6051 } else if (num < 200) {
6052 /* 100 < num < 200 */
6053 snprintf(fn, sizeof(fn), "digits/hundred-100");
6054 num -= ((num / 100) * 100);
6055 }else if (num < 1000) {
6056 /* 200 < num < 1000 */
6057 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6058 num -= ((num / 100) * 100);
6059 }else if (num < 2000){
6060 snprintf(fn, sizeof(fn), "digits/xilia");
6061 num -= ((num / 1000) * 1000);
6063 else {
6064 /* num > 1000 */
6065 if (num < 1000000) {
6066 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6067 if (res)
6068 return res;
6069 num = num % 1000;
6070 snprintf(fn, sizeof(fn), "digits/thousands");
6071 } else {
6072 if (num < 1000000000) { /* 1,000,000,000 */
6073 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6074 if (res)
6075 return res;
6076 num = num % 1000000;
6077 snprintf(fn, sizeof(fn), "digits/millions");
6078 } else {
6079 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6080 res = -1;
6084 if (!res) {
6085 if(!ast_streamfile(chan, fn, language)) {
6086 if ((audiofd > -1) && (ctrlfd > -1))
6087 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6088 else
6089 res = ast_waitstream(chan, ints);
6091 ast_stopstream(chan);
6094 return res;
6099 * The format is weekday - day - month -year
6101 * A list of the files that you need to create
6102 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6103 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6104 Attention the months are in
6105 "gekinh klhsh"
6109 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6111 struct tm tm;
6113 char fn[256];
6114 int res = 0;
6117 ast_localtime(&t,&tm,NULL);
6118 /* W E E K - D A Y */
6119 if (!res) {
6120 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6121 res = ast_streamfile(chan, fn, lang);
6122 if (!res)
6123 res = ast_waitstream(chan, ints);
6125 /* D A Y */
6126 if (!res) {
6127 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6129 /* M O N T H */
6130 if (!res) {
6131 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6132 res = ast_streamfile(chan, fn, lang);
6133 if (!res)
6134 res = ast_waitstream(chan, ints);
6136 /* Y E A R */
6137 if (!res)
6138 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6139 return res;
6144 /* A list of the files that you need to create
6145 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6146 * digits/kai : "KAI"
6147 * didgits : "h wra"
6148 * digits/p-m : "meta meshmbrias"
6149 * digits/a-m : "pro meshmbrias"
6152 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6155 struct tm tm;
6156 int res = 0;
6157 int hour, pm=0;
6159 localtime_r(&t,&tm);
6160 hour = tm.tm_hour;
6162 if (!hour)
6163 hour = 12;
6164 else if (hour == 12)
6165 pm = 1;
6166 else if (hour > 12) {
6167 hour -= 12;
6168 pm = 1;
6171 res = gr_say_number_female(hour, chan, ints, lang);
6172 if (tm.tm_min) {
6173 if (!res)
6174 res = ast_streamfile(chan, "digits/kai", lang);
6175 if (!res)
6176 res = ast_waitstream(chan, ints);
6177 if (!res)
6178 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6179 } else {
6180 if (!res)
6181 res = ast_streamfile(chan, "digits/hwra", lang);
6182 if (!res)
6183 res = ast_waitstream(chan, ints);
6185 if (pm) {
6186 if (!res)
6187 res = ast_streamfile(chan, "digits/p-m", lang);
6188 } else {
6189 if (!res)
6190 res = ast_streamfile(chan, "digits/a-m", lang);
6192 if (!res)
6193 res = ast_waitstream(chan, ints);
6194 return res;
6199 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6201 struct tm tm;
6202 char fn[256];
6203 int res = 0;
6204 localtime_r(&t,&tm);
6207 /* W E E K - D A Y */
6208 if (!res) {
6209 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6210 res = ast_streamfile(chan, fn, lang);
6211 if (!res)
6212 res = ast_waitstream(chan, ints);
6214 /* D A Y */
6215 if (!res) {
6216 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6218 /* M O N T H */
6219 if (!res) {
6220 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6221 res = ast_streamfile(chan, fn, lang);
6222 if (!res)
6223 res = ast_waitstream(chan, ints);
6226 res = ast_say_time_gr(chan, t, ints, lang);
6227 return res;
6230 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)
6233 struct tm tm;
6234 int res=0, offset, sndoffset;
6235 char sndfile[256], nextmsg[256];
6237 if (!format)
6238 format = "AdBY 'digits/at' IMp";
6240 ast_localtime(&time,&tm,timezone);
6242 for (offset=0 ; format[offset] != '\0' ; offset++) {
6243 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6244 switch (format[offset]) {
6245 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6246 case '\'':
6247 /* Literal name of a sound file */
6248 sndoffset=0;
6249 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6250 sndfile[sndoffset] = format[offset];
6251 sndfile[sndoffset] = '\0';
6252 res = wait_file(chan,ints,sndfile,lang);
6253 break;
6254 case 'A':
6255 case 'a':
6256 /* Sunday - Saturday */
6257 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6258 res = wait_file(chan,ints,nextmsg,lang);
6259 break;
6260 case 'B':
6261 case 'b':
6262 case 'h':
6263 /* January - December */
6264 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6265 res = wait_file(chan,ints,nextmsg,lang);
6266 break;
6267 case 'd':
6268 case 'e':
6269 /* first - thirtyfirst */
6270 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6271 break;
6272 case 'Y':
6273 /* Year */
6275 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6276 break;
6277 case 'I':
6278 case 'l':
6279 /* 12-Hour */
6280 if (tm.tm_hour == 0)
6281 gr_say_number_female(12, chan, ints, lang);
6282 else if (tm.tm_hour > 12)
6283 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6284 else
6285 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6286 break;
6287 case 'H':
6288 case 'k':
6289 /* 24-Hour */
6290 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6291 break;
6292 case 'M':
6293 /* Minute */
6294 if (tm.tm_min) {
6295 if (!res)
6296 res = ast_streamfile(chan, "digits/kai", lang);
6297 if (!res)
6298 res = ast_waitstream(chan, ints);
6299 if (!res)
6300 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6301 } else {
6302 if (!res)
6303 res = ast_streamfile(chan, "digits/oclock", lang);
6304 if (!res)
6305 res = ast_waitstream(chan, ints);
6307 break;
6308 case 'P':
6309 case 'p':
6310 /* AM/PM */
6311 if (tm.tm_hour > 11)
6312 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6313 else
6314 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6315 res = wait_file(chan,ints,nextmsg,lang);
6316 break;
6317 case 'Q':
6318 /* Shorthand for "Today", "Yesterday", or ABdY */
6319 /* XXX As emphasized elsewhere, this should the native way in your
6320 * language to say the date, with changes in what you say, depending
6321 * upon how recent the date is. XXX */
6323 struct timeval now;
6324 struct tm tmnow;
6325 time_t beg_today, tt;
6327 gettimeofday(&now,NULL);
6328 tt = now.tv_sec;
6329 ast_localtime(&tt,&tmnow,timezone);
6330 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6331 /* In any case, it saves not having to do ast_mktime() */
6332 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6333 if (beg_today < time) {
6334 /* Today */
6335 res = wait_file(chan,ints, "digits/today",lang);
6336 } else if (beg_today - 86400 < time) {
6337 /* Yesterday */
6338 res = wait_file(chan,ints, "digits/yesterday",lang);
6339 } else {
6340 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6343 break;
6344 case 'q':
6345 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6346 /* XXX As emphasized elsewhere, this should the native way in your
6347 * language to say the date, with changes in what you say, depending
6348 * upon how recent the date is. XXX */
6350 struct timeval now;
6351 struct tm tmnow;
6352 time_t beg_today, tt;
6354 gettimeofday(&now,NULL);
6355 tt = now.tv_sec;
6356 ast_localtime(&tt,&tmnow,timezone);
6357 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6358 /* In any case, it saves not having to do ast_mktime() */
6359 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6360 if (beg_today < time) {
6361 /* Today */
6362 } else if ((beg_today - 86400) < time) {
6363 /* Yesterday */
6364 res = wait_file(chan,ints, "digits/yesterday",lang);
6365 } else if (beg_today - 86400 * 6 < time) {
6366 /* Within the last week */
6367 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6368 } else {
6369 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6372 break;
6373 case 'R':
6374 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6375 break;
6376 case 'S':
6377 /* Seconds */
6378 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6379 res = wait_file(chan,ints,nextmsg,lang);
6380 if (!res)
6381 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6382 if (!res)
6383 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6384 res = wait_file(chan,ints,nextmsg,lang);
6385 break;
6386 case 'T':
6387 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6388 break;
6389 case ' ':
6390 case ' ':
6391 /* Just ignore spaces and tabs */
6392 break;
6393 default:
6394 /* Unknown character */
6395 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6397 /* Jump out on DTMF */
6398 if (res) {
6399 break;
6402 return res;
6406 * remap the 'say' functions to use those in this file
6408 static void __attribute__((constructor)) __say_init(void)
6410 ast_say_number_full = say_number_full;
6411 ast_say_enumeration_full = say_enumeration_full;
6412 ast_say_digit_str_full = say_digit_str_full;
6413 ast_say_character_str_full = say_character_str_full;
6414 ast_say_phonetic_str_full = say_phonetic_str_full;
6415 ast_say_datetime = say_datetime;
6416 ast_say_time = say_time;
6417 ast_say_date = say_date;
6418 ast_say_datetime_from_now = say_datetime_from_now;
6419 ast_say_date_with_format = say_date_with_format;