make Local channel return sensible device state values
[asterisk-bristuff.git] / say.c
blob1e9ed5407a5a403512c38d0cd7dbb85488a132b4
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 > 12)
3880 res = wait_file(chan, ints, "digits/p-m", lang);
3881 else if (tm.tm_hour && tm.tm_hour < 12)
3882 res = wait_file(chan, ints, "digits/a-m", lang);
3883 break;
3884 case 'Q':
3885 /* Shorthand for "Today", "Yesterday", or ABdY */
3886 /* XXX As emphasized elsewhere, this should the native way in your
3887 * language to say the date, with changes in what you say, depending
3888 * upon how recent the date is. XXX */
3890 struct timeval now;
3891 struct tm tmnow;
3892 time_t beg_today, tt;
3894 gettimeofday(&now,NULL);
3895 tt = now.tv_sec;
3896 ast_localtime(&tt,&tmnow,timezone);
3897 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3898 /* In any case, it saves not having to do ast_mktime() */
3899 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3900 if (beg_today < time) {
3901 /* Today */
3902 res = wait_file(chan,ints, "digits/today",lang);
3903 } else if (beg_today - 86400 < time) {
3904 /* Yesterday */
3905 res = wait_file(chan,ints, "digits/yesterday",lang);
3906 } else {
3907 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3910 break;
3911 case 'q':
3912 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3913 /* XXX As emphasized elsewhere, this should the native way in your
3914 * language to say the date, with changes in what you say, depending
3915 * upon how recent the date is. XXX */
3917 struct timeval now;
3918 struct tm tmnow;
3919 time_t beg_today, tt;
3921 gettimeofday(&now,NULL);
3922 tt = now.tv_sec;
3923 ast_localtime(&tt,&tmnow,timezone);
3924 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3925 /* In any case, it saves not having to do ast_mktime() */
3926 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3927 if (beg_today < time) {
3928 /* Today */
3929 res = wait_file(chan,ints, "digits/today",lang);
3930 } else if ((beg_today - 86400) < time) {
3931 /* Yesterday */
3932 res = wait_file(chan,ints, "digits/yesterday",lang);
3933 } else if (beg_today - 86400 * 6 < time) {
3934 /* Within the last week */
3935 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
3936 } else {
3937 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3940 break;
3941 case 'R':
3942 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
3943 break;
3944 case 'S':
3945 /* Seconds */
3946 if (tm.tm_sec == 0) {
3947 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3948 res = wait_file(chan,ints,nextmsg,lang);
3949 } else if (tm.tm_sec < 10) {
3950 res = wait_file(chan,ints, "digits/oh",lang);
3951 if (!res) {
3952 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3953 res = wait_file(chan,ints,nextmsg,lang);
3955 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
3956 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3957 res = wait_file(chan,ints,nextmsg,lang);
3958 } else {
3959 int ten, one;
3960 ten = (tm.tm_sec / 10) * 10;
3961 one = (tm.tm_sec % 10);
3962 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
3963 res = wait_file(chan,ints,nextmsg,lang);
3964 if (!res) {
3965 /* Fifty, not fifty-zero */
3966 if (one != 0) {
3967 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
3968 res = wait_file(chan,ints,nextmsg,lang);
3972 break;
3973 case 'T':
3974 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
3975 break;
3976 case ' ':
3977 case ' ':
3978 /* Just ignore spaces and tabs */
3979 break;
3980 default:
3981 /* Unknown character */
3982 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3984 /* Jump out on DTMF */
3985 if (res) {
3986 break;
3989 return res;
3992 /* French syntax
3993 oclock = heure
3995 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)
3997 struct tm tm;
3998 int res=0, offset, sndoffset;
3999 char sndfile[256], nextmsg[256];
4001 if (format == NULL)
4002 format = "AdBY 'digits/at' IMp";
4004 ast_localtime(&time,&tm,timezone);
4006 for (offset=0 ; format[offset] != '\0' ; offset++) {
4007 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4008 switch (format[offset]) {
4009 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4010 case '\'':
4011 /* Literal name of a sound file */
4012 sndoffset=0;
4013 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4014 sndfile[sndoffset] = format[offset];
4015 sndfile[sndoffset] = '\0';
4016 res = wait_file(chan,ints,sndfile,lang);
4017 break;
4018 case 'A':
4019 case 'a':
4020 /* Sunday - Saturday */
4021 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4022 res = wait_file(chan,ints,nextmsg,lang);
4023 break;
4024 case 'B':
4025 case 'b':
4026 case 'h':
4027 /* January - December */
4028 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4029 res = wait_file(chan,ints,nextmsg,lang);
4030 break;
4031 case 'm':
4032 /* First - Twelfth */
4033 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4034 res = wait_file(chan,ints,nextmsg,lang);
4035 break;
4036 case 'd':
4037 case 'e':
4038 /* First */
4039 if (tm.tm_mday == 1) {
4040 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4041 res = wait_file(chan,ints,nextmsg,lang);
4042 } else {
4043 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4045 break;
4046 case 'Y':
4047 /* Year */
4048 if (tm.tm_year > 99) {
4049 res = wait_file(chan,ints, "digits/2",lang);
4050 if (!res) {
4051 res = wait_file(chan,ints, "digits/thousand",lang);
4053 if (tm.tm_year > 100) {
4054 if (!res) {
4055 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4058 } else {
4059 if (tm.tm_year < 1) {
4060 /* I'm not going to handle 1900 and prior */
4061 /* We'll just be silent on the year, instead of bombing out. */
4062 } else {
4063 res = wait_file(chan,ints, "digits/thousand",lang);
4064 if (!res) {
4065 wait_file(chan,ints, "digits/9",lang);
4066 wait_file(chan,ints, "digits/hundred",lang);
4067 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4071 break;
4072 case 'I':
4073 case 'l':
4074 /* 12-Hour */
4075 if (tm.tm_hour == 0)
4076 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4077 else if (tm.tm_hour > 12)
4078 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4079 else
4080 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4081 res = wait_file(chan,ints,nextmsg,lang);
4082 if (!res)
4083 res = wait_file(chan,ints, "digits/oclock",lang);
4084 break;
4085 case 'H':
4086 case 'k':
4087 /* 24-Hour */
4088 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4089 if (!res)
4090 res = wait_file(chan,ints, "digits/oclock",lang);
4091 break;
4092 case 'M':
4093 /* Minute */
4094 if (tm.tm_min == 0) {
4095 break;
4097 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4098 break;
4099 case 'P':
4100 case 'p':
4101 /* AM/PM */
4102 if (tm.tm_hour > 11)
4103 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4104 else
4105 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4106 res = wait_file(chan,ints,nextmsg,lang);
4107 break;
4108 case 'Q':
4109 /* Shorthand for "Today", "Yesterday", or AdBY */
4110 /* XXX As emphasized elsewhere, this should the native way in your
4111 * language to say the date, with changes in what you say, depending
4112 * upon how recent the date is. XXX */
4114 struct timeval now;
4115 struct tm tmnow;
4116 time_t beg_today, tt;
4118 gettimeofday(&now,NULL);
4119 tt = now.tv_sec;
4120 ast_localtime(&tt,&tmnow,timezone);
4121 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4122 /* In any case, it saves not having to do ast_mktime() */
4123 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4124 if (beg_today < time) {
4125 /* Today */
4126 res = wait_file(chan,ints, "digits/today",lang);
4127 } else if (beg_today - 86400 < time) {
4128 /* Yesterday */
4129 res = wait_file(chan,ints, "digits/yesterday",lang);
4130 } else {
4131 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4134 break;
4135 case 'q':
4136 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4137 /* XXX As emphasized elsewhere, this should the native way in your
4138 * language to say the date, with changes in what you say, depending
4139 * upon how recent the date is. XXX */
4141 struct timeval now;
4142 struct tm tmnow;
4143 time_t beg_today, tt;
4145 gettimeofday(&now,NULL);
4146 tt = now.tv_sec;
4147 ast_localtime(&tt,&tmnow,timezone);
4148 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4149 /* In any case, it saves not having to do ast_mktime() */
4150 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4151 if (beg_today < time) {
4152 /* Today */
4153 } else if ((beg_today - 86400) < time) {
4154 /* Yesterday */
4155 res = wait_file(chan,ints, "digits/yesterday",lang);
4156 } else if (beg_today - 86400 * 6 < time) {
4157 /* Within the last week */
4158 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4159 } else {
4160 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4163 break;
4164 case 'R':
4165 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4166 break;
4167 case 'S':
4168 /* Seconds */
4169 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4170 if (!res) {
4171 res = wait_file(chan,ints, "digits/second",lang);
4173 break;
4174 case 'T':
4175 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4176 break;
4177 case ' ':
4178 case ' ':
4179 /* Just ignore spaces and tabs */
4180 break;
4181 default:
4182 /* Unknown character */
4183 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4185 /* Jump out on DTMF */
4186 if (res) {
4187 break;
4190 return res;
4193 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)
4195 struct tm tm;
4196 int res=0, offset, sndoffset;
4197 char sndfile[256], nextmsg[256];
4199 if (format == NULL)
4200 format = "AdB 'digits/at' IMp";
4202 ast_localtime(&time,&tm,timezone);
4204 for (offset=0 ; format[offset] != '\0' ; offset++) {
4205 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4206 switch (format[offset]) {
4207 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4208 case '\'':
4209 /* Literal name of a sound file */
4210 sndoffset=0;
4211 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4212 sndfile[sndoffset] = format[offset];
4213 sndfile[sndoffset] = '\0';
4214 res = wait_file(chan,ints,sndfile,lang);
4215 break;
4216 case 'A':
4217 case 'a':
4218 /* Sunday - Saturday */
4219 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4220 res = wait_file(chan,ints,nextmsg,lang);
4221 break;
4222 case 'B':
4223 case 'b':
4224 case 'h':
4225 /* January - December */
4226 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4227 res = wait_file(chan,ints,nextmsg,lang);
4228 break;
4229 case 'm':
4230 /* First - Twelfth */
4231 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4232 res = wait_file(chan,ints,nextmsg,lang);
4233 break;
4234 case 'd':
4235 case 'e':
4236 /* First day of the month is spelled as ordinal */
4237 if (tm.tm_mday == 1) {
4238 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4239 res = wait_file(chan,ints,nextmsg,lang);
4240 } else {
4241 if (!res) {
4242 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4245 break;
4246 case 'Y':
4247 /* Year */
4248 if (tm.tm_year > 99) {
4249 res = wait_file(chan,ints, "digits/ore-2000",lang);
4250 if (tm.tm_year > 100) {
4251 if (!res) {
4252 /* This works until the end of 2021 */
4253 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4254 res = wait_file(chan,ints,nextmsg,lang);
4257 } else {
4258 if (tm.tm_year < 1) {
4259 /* I'm not going to handle 1900 and prior */
4260 /* We'll just be silent on the year, instead of bombing out. */
4261 } else {
4262 res = wait_file(chan,ints, "digits/ore-1900",lang);
4263 if ((!res) && (tm.tm_year != 0)) {
4264 if (tm.tm_year <= 21) {
4265 /* 1910 - 1921 */
4266 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4267 res = wait_file(chan,ints,nextmsg,lang);
4268 } else {
4269 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4270 int ten, one;
4271 ten = tm.tm_year / 10;
4272 one = tm.tm_year % 10;
4273 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4274 res = wait_file(chan,ints,nextmsg,lang);
4275 if (!res) {
4276 if (one != 0) {
4277 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4278 res = wait_file(chan,ints,nextmsg,lang);
4285 break;
4286 case 'I':
4287 case 'l':
4288 /* 12-Hour */
4289 if (tm.tm_hour == 0)
4290 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4291 else if (tm.tm_hour > 12)
4292 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4293 else
4294 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4295 res = wait_file(chan,ints,nextmsg,lang);
4296 break;
4297 case 'H':
4298 case 'k':
4299 /* 24-Hour */
4300 if (tm.tm_hour == 0) {
4301 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4302 } else if (tm.tm_hour == 1) {
4303 res = wait_file(chan,ints, "digits/ore-una",lang);
4304 } else {
4305 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4307 break;
4308 case 'M':
4309 /* Minute */
4310 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4311 break;
4312 case 'P':
4313 case 'p':
4314 /* AM/PM */
4315 if (tm.tm_hour > 11)
4316 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4317 else
4318 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4319 res = wait_file(chan,ints,nextmsg,lang);
4320 break;
4321 case 'Q':
4322 /* Shorthand for "Today", "Yesterday", or ABdY */
4323 /* XXX As emphasized elsewhere, this should the native way in your
4324 * language to say the date, with changes in what you say, depending
4325 * upon how recent the date is. XXX */
4327 struct timeval now;
4328 struct tm tmnow;
4329 time_t beg_today, tt;
4331 gettimeofday(&now,NULL);
4332 tt = now.tv_sec;
4333 ast_localtime(&tt,&tmnow,timezone);
4334 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4335 /* In any case, it saves not having to do ast_mktime() */
4336 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4337 if (beg_today < time) {
4338 /* Today */
4339 res = wait_file(chan,ints, "digits/today",lang);
4340 } else if (beg_today - 86400 < time) {
4341 /* Yesterday */
4342 res = wait_file(chan,ints, "digits/yesterday",lang);
4343 } else {
4344 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4347 break;
4348 case 'q':
4349 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4351 struct timeval now;
4352 struct tm tmnow;
4353 time_t beg_today, tt;
4355 gettimeofday(&now,NULL);
4356 tt = now.tv_sec;
4357 ast_localtime(&tt,&tmnow,timezone);
4358 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4359 /* In any case, it saves not having to do ast_mktime() */
4360 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4361 if (beg_today < time) {
4362 /* Today */
4363 } else if ((beg_today - 86400) < time) {
4364 /* Yesterday */
4365 res = wait_file(chan,ints, "digits/yesterday",lang);
4366 } else if (beg_today - 86400 * 6 < time) {
4367 /* Within the last week */
4368 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4369 } else {
4370 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4373 break;
4374 case 'R':
4375 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4376 break;
4377 case 'S':
4378 /* Seconds */
4379 if (tm.tm_sec == 0) {
4380 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4381 res = wait_file(chan,ints,nextmsg,lang);
4382 } else if (tm.tm_sec < 10) {
4383 res = wait_file(chan,ints, "digits/oh",lang);
4384 if (!res) {
4385 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4386 res = wait_file(chan,ints,nextmsg,lang);
4388 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4389 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4390 res = wait_file(chan,ints,nextmsg,lang);
4391 } else {
4392 int ten, one;
4393 ten = (tm.tm_sec / 10) * 10;
4394 one = (tm.tm_sec % 10);
4395 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4396 res = wait_file(chan,ints,nextmsg,lang);
4397 if (!res) {
4398 /* Fifty, not fifty-zero */
4399 if (one != 0) {
4400 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4401 res = wait_file(chan,ints,nextmsg,lang);
4405 break;
4406 case 'T':
4407 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4408 break;
4409 case ' ':
4410 case ' ':
4411 /* Just ignore spaces and tabs */
4412 break;
4413 default:
4414 /* Unknown character */
4415 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4417 /* Jump out on DTMF */
4418 if (res) {
4419 break;
4422 return res;
4425 /* Dutch syntax */
4426 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)
4428 struct tm tm;
4429 int res=0, offset, sndoffset;
4430 char sndfile[256], nextmsg[256];
4432 if (format == NULL)
4433 format = "ABdY 'digits/at' IMp";
4435 ast_localtime(&time,&tm,timezone);
4437 for (offset=0 ; format[offset] != '\0' ; offset++) {
4438 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4439 switch (format[offset]) {
4440 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4441 case '\'':
4442 /* Literal name of a sound file */
4443 sndoffset=0;
4444 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4445 sndfile[sndoffset] = format[offset];
4446 sndfile[sndoffset] = '\0';
4447 res = wait_file(chan,ints,sndfile,lang);
4448 break;
4449 case 'A':
4450 case 'a':
4451 /* Sunday - Saturday */
4452 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4453 res = wait_file(chan,ints,nextmsg,lang);
4454 break;
4455 case 'B':
4456 case 'b':
4457 case 'h':
4458 /* January - December */
4459 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4460 res = wait_file(chan,ints,nextmsg,lang);
4461 break;
4462 case 'm':
4463 /* First - Twelfth */
4464 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4465 res = wait_file(chan,ints,nextmsg,lang);
4466 break;
4467 case 'd':
4468 case 'e':
4469 /* First - Thirtyfirst */
4470 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4471 break;
4472 case 'Y':
4473 /* Year */
4474 if (tm.tm_year > 99) {
4475 res = wait_file(chan,ints, "digits/2",lang);
4476 if (!res) {
4477 res = wait_file(chan,ints, "digits/thousand",lang);
4479 if (tm.tm_year > 100) {
4480 if (!res) {
4481 /* This works until the end of 2020 */
4482 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4483 res = wait_file(chan,ints,nextmsg,lang);
4486 } else {
4487 if (tm.tm_year < 1) {
4488 /* I'm not going to handle 1900 and prior */
4489 /* We'll just be silent on the year, instead of bombing out. */
4490 } else {
4491 res = wait_file(chan,ints, "digits/19",lang);
4492 if (!res) {
4493 if (tm.tm_year <= 9) {
4494 /* 1901 - 1909 */
4495 res = wait_file(chan,ints, "digits/oh",lang);
4496 if (!res) {
4497 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4498 res = wait_file(chan,ints,nextmsg,lang);
4500 } else if (tm.tm_year <= 20) {
4501 /* 1910 - 1920 */
4502 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4503 res = wait_file(chan,ints,nextmsg,lang);
4504 } else {
4505 /* 1921 - 1999 */
4506 int ten, one;
4507 ten = tm.tm_year / 10;
4508 one = tm.tm_year % 10;
4509 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4510 res = wait_file(chan,ints,nextmsg,lang);
4511 if (!res) {
4512 if (one != 0) {
4513 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4514 res = wait_file(chan,ints,nextmsg,lang);
4521 break;
4522 case 'I':
4523 case 'l':
4524 /* 12-Hour */
4525 if (tm.tm_hour == 0)
4526 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4527 else if (tm.tm_hour > 12)
4528 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4529 else
4530 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4531 res = wait_file(chan,ints,nextmsg,lang);
4532 break;
4533 case 'H':
4534 case 'k':
4535 /* 24-Hour */
4536 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4537 if (!res) {
4538 res = wait_file(chan,ints, "digits/nl-uur",lang);
4540 break;
4541 case 'M':
4542 /* Minute */
4543 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4544 break;
4545 case 'P':
4546 case 'p':
4547 /* AM/PM */
4548 if (tm.tm_hour > 11)
4549 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4550 else
4551 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4552 res = wait_file(chan,ints,nextmsg,lang);
4553 break;
4554 case 'Q':
4555 /* Shorthand for "Today", "Yesterday", or ABdY */
4556 /* XXX As emphasized elsewhere, this should the native way in your
4557 * language to say the date, with changes in what you say, depending
4558 * upon how recent the date is. XXX */
4560 struct timeval now;
4561 struct tm tmnow;
4562 time_t beg_today, tt;
4564 gettimeofday(&now,NULL);
4565 tt = now.tv_sec;
4566 ast_localtime(&tt,&tmnow,timezone);
4567 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4568 /* In any case, it saves not having to do ast_mktime() */
4569 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4570 if (beg_today < time) {
4571 /* Today */
4572 res = wait_file(chan,ints, "digits/today",lang);
4573 } else if (beg_today - 86400 < time) {
4574 /* Yesterday */
4575 res = wait_file(chan,ints, "digits/yesterday",lang);
4576 } else {
4577 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4580 break;
4581 case 'q':
4582 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4584 struct timeval now;
4585 struct tm tmnow;
4586 time_t beg_today, tt;
4588 gettimeofday(&now,NULL);
4589 tt = now.tv_sec;
4590 ast_localtime(&tt,&tmnow,timezone);
4591 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4592 /* In any case, it saves not having to do ast_mktime() */
4593 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4594 if (beg_today < time) {
4595 /* Today */
4596 } else if ((beg_today - 86400) < time) {
4597 /* Yesterday */
4598 res = wait_file(chan,ints, "digits/yesterday",lang);
4599 } else if (beg_today - 86400 * 6 < time) {
4600 /* Within the last week */
4601 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4602 } else {
4603 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4606 break;
4607 case 'R':
4608 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4609 break;
4610 case 'S':
4611 /* Seconds */
4612 if (tm.tm_sec == 0) {
4613 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4614 res = wait_file(chan,ints,nextmsg,lang);
4615 } else if (tm.tm_sec < 10) {
4616 res = wait_file(chan,ints, "digits/oh",lang);
4617 if (!res) {
4618 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4619 res = wait_file(chan,ints,nextmsg,lang);
4621 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4622 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4623 res = wait_file(chan,ints,nextmsg,lang);
4624 } else {
4625 int ten, one;
4626 ten = (tm.tm_sec / 10) * 10;
4627 one = (tm.tm_sec % 10);
4628 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4629 res = wait_file(chan,ints,nextmsg,lang);
4630 if (!res) {
4631 /* Fifty, not fifty-zero */
4632 if (one != 0) {
4633 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4634 res = wait_file(chan,ints,nextmsg,lang);
4638 break;
4639 case 'T':
4640 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4641 break;
4642 case ' ':
4643 case ' ':
4644 /* Just ignore spaces and tabs */
4645 break;
4646 default:
4647 /* Unknown character */
4648 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4650 /* Jump out on DTMF */
4651 if (res) {
4652 break;
4655 return res;
4658 /* Polish syntax */
4659 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)
4661 struct tm tm;
4662 int res=0, offset, sndoffset;
4663 char sndfile[256], nextmsg[256];
4665 ast_localtime(&thetime, &tm, timezone);
4667 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4668 int remainder;
4669 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4670 switch (format[offset]) {
4671 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4672 case '\'':
4673 /* Literal name of a sound file */
4674 sndoffset = 0;
4675 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4676 sndfile[sndoffset] = format[offset];
4677 sndfile[sndoffset] = '\0';
4678 res = wait_file(chan, ints, sndfile, lang);
4679 break;
4680 case 'A':
4681 case 'a':
4682 /* Sunday - Saturday */
4683 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4684 res = wait_file(chan, ints, nextmsg, lang);
4685 break;
4686 case 'B':
4687 case 'b':
4688 case 'h':
4689 /* January - December */
4690 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4691 res = wait_file(chan, ints, nextmsg, lang);
4692 break;
4693 case 'm':
4694 /* Month enumerated */
4695 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4696 break;
4697 case 'd':
4698 case 'e':
4699 /* First - Thirtyfirst */
4700 remainder = tm.tm_mday;
4701 if (tm.tm_mday > 20) {
4702 res = wait_file(chan, ints, "digits/h-20", lang);
4703 remainder -= 20;
4705 if (!res) {
4706 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4707 res = wait_file(chan, ints, nextmsg, lang);
4709 break;
4710 case 'Y':
4711 /* Year */
4712 if (tm.tm_year > 100) {
4713 res = wait_file(chan, ints, "digits/2", lang);
4714 if (!res)
4715 res = wait_file(chan, ints, "digits/1000.2",lang);
4716 if (tm.tm_year > 100) {
4717 if (!res)
4718 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4720 } else if (tm.tm_year == 100) {
4721 res = wait_file(chan, ints, "digits/h-2000", lang);
4722 } else {
4723 if (tm.tm_year < 1) {
4724 /* I'm not going to handle 1900 and prior */
4725 /* We'll just be silent on the year, instead of bombing out. */
4726 break;
4727 } else {
4728 res = wait_file(chan, ints, "digits/1000", lang);
4729 if (!res) {
4730 wait_file(chan, ints, "digits/900", lang);
4731 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4735 if (!res)
4736 wait_file(chan, ints, "digits/year", lang);
4737 break;
4738 case 'I':
4739 case 'l':
4740 /* 12-Hour */
4741 if (tm.tm_hour == 0)
4742 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4743 else if (tm.tm_hour > 12)
4744 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4745 else
4746 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4748 res = wait_file(chan, ints, nextmsg, lang);
4749 break;
4750 case 'H':
4751 case 'k':
4752 /* 24-Hour */
4753 if (tm.tm_hour != 0) {
4754 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4755 res = wait_file(chan, ints, nextmsg, lang);
4756 } else
4757 res = wait_file(chan, ints, "digits/t-24", lang);
4758 break;
4759 case 'M':
4760 case 'N':
4761 /* Minute */
4762 if (tm.tm_min == 0) {
4763 if (format[offset] == 'M') {
4764 res = wait_file(chan, ints, "digits/oclock", lang);
4765 } else {
4766 res = wait_file(chan, ints, "digits/100", lang);
4768 } else
4769 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4770 break;
4771 case 'P':
4772 case 'p':
4773 /* AM/PM */
4774 if (tm.tm_hour > 11)
4775 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4776 else
4777 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4778 res = wait_file(chan, ints, nextmsg, lang);
4779 break;
4780 case 'Q':
4781 /* Shorthand for "Today", "Yesterday", or AdBY */
4783 time_t tv_sec = time(NULL);
4784 struct tm tmnow;
4785 time_t beg_today;
4787 ast_localtime(&tv_sec,&tmnow, timezone);
4788 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4789 /* In any case, it saves not having to do ast_mktime() */
4790 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4791 if (beg_today < thetime) {
4792 /* Today */
4793 res = wait_file(chan, ints, "digits/today", lang);
4794 } else if (beg_today - 86400 < thetime) {
4795 /* Yesterday */
4796 res = wait_file(chan, ints, "digits/yesterday", lang);
4797 } else {
4798 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4801 break;
4802 case 'q':
4803 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4805 time_t tv_sec = time(NULL);
4806 struct tm tmnow;
4807 time_t beg_today;
4809 ast_localtime(&tv_sec, &tmnow, timezone);
4810 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4811 /* In any case, it saves not having to do ast_mktime() */
4812 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4813 if (beg_today < thetime) {
4814 /* Today */
4815 } else if ((beg_today - 86400) < thetime) {
4816 /* Yesterday */
4817 res = wait_file(chan, ints, "digits/yesterday", lang);
4818 } else if (beg_today - 86400 * 6 < thetime) {
4819 /* Within the last week */
4820 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
4821 } else {
4822 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4825 break;
4826 case 'R':
4827 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
4828 break;
4829 case 'S':
4830 /* Seconds */
4831 res = wait_file(chan, ints, "digits/and", lang);
4832 if (!res) {
4833 if (tm.tm_sec == 1) {
4834 res = wait_file(chan, ints, "digits/1z", lang);
4835 if (!res)
4836 res = wait_file(chan, ints, "digits/second-a", lang);
4837 } else {
4838 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4839 if (!res) {
4840 int ten, one;
4841 ten = tm.tm_sec / 10;
4842 one = tm.tm_sec % 10;
4844 if (one > 1 && one < 5 && ten != 1)
4845 res = wait_file(chan,ints, "digits/seconds",lang);
4846 else
4847 res = wait_file(chan,ints, "digits/second",lang);
4851 break;
4852 case 'T':
4853 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
4854 break;
4855 case ' ':
4856 case ' ':
4857 /* Just ignore spaces and tabs */
4858 break;
4859 default:
4860 /* Unknown character */
4861 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4863 /* Jump out on DTMF */
4864 if (res)
4865 break;
4867 return res;
4870 /* Portuguese syntax */
4871 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)
4873 struct tm tm;
4874 int res=0, offset, sndoffset;
4875 char sndfile[256], nextmsg[256];
4877 if (format == NULL)
4878 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4880 ast_localtime(&time,&tm,timezone);
4882 for (offset=0 ; format[offset] != '\0' ; offset++) {
4883 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4884 switch (format[offset]) {
4885 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4886 case '\'':
4887 /* Literal name of a sound file */
4888 sndoffset=0;
4889 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4890 sndfile[sndoffset] = format[offset];
4891 sndfile[sndoffset] = '\0';
4892 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4893 res = wait_file(chan,ints,nextmsg,lang);
4894 break;
4895 case 'A':
4896 case 'a':
4897 /* Sunday - Saturday */
4898 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4899 res = wait_file(chan,ints,nextmsg,lang);
4900 break;
4901 case 'B':
4902 case 'b':
4903 case 'h':
4904 /* January - December */
4905 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4906 res = wait_file(chan,ints,nextmsg,lang);
4907 break;
4908 case 'm':
4909 /* First - Twelfth */
4910 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4911 res = wait_file(chan,ints,nextmsg,lang);
4912 break;
4913 case 'd':
4914 case 'e':
4915 /* First - Thirtyfirst */
4916 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4917 break;
4918 case 'Y':
4919 /* Year */
4920 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4921 break;
4922 case 'I':
4923 case 'l':
4924 /* 12-Hour */
4925 if (tm.tm_hour == 0) {
4926 if (format[offset] == 'I')
4927 res = wait_file(chan, ints, "digits/pt-ah", lang);
4928 if (!res)
4929 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4931 else if (tm.tm_hour == 12) {
4932 if (format[offset] == 'I')
4933 res = wait_file(chan, ints, "digits/pt-ao", lang);
4934 if (!res)
4935 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4937 else {
4938 if (format[offset] == 'I') {
4939 res = wait_file(chan, ints, "digits/pt-ah", lang);
4940 if ((tm.tm_hour % 12) != 1)
4941 if (!res)
4942 res = wait_file(chan, ints, "digits/pt-sss", lang);
4944 if (!res)
4945 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
4947 break;
4948 case 'H':
4949 case 'k':
4950 /* 24-Hour */
4951 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
4952 if (!res) {
4953 if (tm.tm_hour != 0) {
4954 int remainder = tm.tm_hour;
4955 if (tm.tm_hour > 20) {
4956 res = wait_file(chan,ints, "digits/20",lang);
4957 remainder -= 20;
4959 if (!res) {
4960 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
4961 res = wait_file(chan,ints,nextmsg,lang);
4965 break;
4966 case 'M':
4967 /* Minute */
4968 if (tm.tm_min == 0) {
4969 res = wait_file(chan, ints, "digits/pt-hora", lang);
4970 if (tm.tm_hour != 1)
4971 if (!res)
4972 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
4973 res = wait_file(chan,ints,"digits/pt-e",lang);
4974 if (!res)
4975 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4977 break;
4978 case 'P':
4979 case 'p':
4980 /* AM/PM */
4981 if (tm.tm_hour > 12)
4982 res = wait_file(chan, ints, "digits/p-m", lang);
4983 else if (tm.tm_hour && tm.tm_hour < 12)
4984 res = wait_file(chan, ints, "digits/a-m", lang);
4985 break;
4986 case 'Q':
4987 /* Shorthand for "Today", "Yesterday", or ABdY */
4988 /* XXX As emphasized elsewhere, this should the native way in your
4989 * language to say the date, with changes in what you say, depending
4990 * upon how recent the date is. XXX */
4992 struct timeval now;
4993 struct tm tmnow;
4994 time_t beg_today, tt;
4996 gettimeofday(&now,NULL);
4997 tt = now.tv_sec;
4998 ast_localtime(&tt,&tmnow,timezone);
4999 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5000 /* In any case, it saves not having to do ast_mktime() */
5001 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5002 if (beg_today < time) {
5003 /* Today */
5004 res = wait_file(chan,ints, "digits/today",lang);
5005 } else if (beg_today - 86400 < time) {
5006 /* Yesterday */
5007 res = wait_file(chan,ints, "digits/yesterday",lang);
5008 } else {
5009 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5012 break;
5013 case 'q':
5014 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5015 /* XXX As emphasized elsewhere, this should the native way in your
5016 * language to say the date, with changes in what you say, depending
5017 * upon how recent the date is. XXX */
5019 struct timeval now;
5020 struct tm tmnow;
5021 time_t beg_today, tt;
5023 gettimeofday(&now,NULL);
5024 tt = now.tv_sec;
5025 ast_localtime(&tt,&tmnow,timezone);
5026 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5027 /* In any case, it saves not having to do ast_mktime() */
5028 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5029 if (beg_today < time) {
5030 /* Today */
5031 } else if ((beg_today - 86400) < time) {
5032 /* Yesterday */
5033 res = wait_file(chan,ints, "digits/yesterday",lang);
5034 } else if (beg_today - 86400 * 6 < time) {
5035 /* Within the last week */
5036 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5037 } else {
5038 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5041 break;
5042 case 'R':
5043 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5044 break;
5045 case 'S':
5046 /* Seconds */
5047 if (tm.tm_sec == 0) {
5048 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5049 res = wait_file(chan,ints,nextmsg,lang);
5050 } else if (tm.tm_sec < 10) {
5051 res = wait_file(chan,ints, "digits/oh",lang);
5052 if (!res) {
5053 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5054 res = wait_file(chan,ints,nextmsg,lang);
5056 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5057 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5058 res = wait_file(chan,ints,nextmsg,lang);
5059 } else {
5060 int ten, one;
5061 ten = (tm.tm_sec / 10) * 10;
5062 one = (tm.tm_sec % 10);
5063 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5064 res = wait_file(chan,ints,nextmsg,lang);
5065 if (!res) {
5066 /* Fifty, not fifty-zero */
5067 if (one != 0) {
5068 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5069 res = wait_file(chan,ints,nextmsg,lang);
5073 break;
5074 case 'T':
5075 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5076 break;
5077 case ' ':
5078 case ' ':
5079 /* Just ignore spaces and tabs */
5080 break;
5081 default:
5082 /* Unknown character */
5083 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5085 /* Jump out on DTMF */
5086 if (res) {
5087 break;
5090 return res;
5093 /* Taiwanese / Chinese syntax */
5094 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)
5096 struct tm tm;
5097 int res=0, offset, sndoffset;
5098 char sndfile[256], nextmsg[256];
5100 if (format == NULL)
5101 format = "YBdA 'digits/at' HM";
5103 ast_localtime(&time,&tm,timezone);
5105 for (offset=0 ; format[offset] != '\0' ; offset++) {
5106 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5107 switch (format[offset]) {
5108 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5109 case '\'':
5110 /* Literal name of a sound file */
5111 sndoffset=0;
5112 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5113 sndfile[sndoffset] = format[offset];
5114 sndfile[sndoffset] = '\0';
5115 res = wait_file(chan,ints,sndfile,lang);
5116 break;
5117 case 'A':
5118 case 'a':
5119 /* Sunday - Saturday */
5120 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5121 res = wait_file(chan,ints,nextmsg,lang);
5122 break;
5123 case 'B':
5124 case 'b':
5125 case 'h':
5126 /* January - December */
5127 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5128 res = wait_file(chan,ints,nextmsg,lang);
5129 break;
5130 case 'm':
5131 /* First - Twelfth */
5132 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5133 res = wait_file(chan,ints,nextmsg,lang);
5134 break;
5135 case 'd':
5136 case 'e':
5137 /* First - Thirtyfirst */
5138 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5139 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5140 res = wait_file(chan,ints,nextmsg,lang);
5141 } else {
5142 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%dh", tm.tm_mday - (tm.tm_mday % 10));
5143 res = wait_file(chan,ints,nextmsg,lang);
5144 if(!res) {
5145 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday % 10);
5146 res = wait_file(chan,ints,nextmsg,lang);
5149 break;
5150 case 'Y':
5151 /* Year */
5152 if (tm.tm_year > 99) {
5153 res = wait_file(chan,ints, "digits/2",lang);
5154 if (!res) {
5155 res = wait_file(chan,ints, "digits/thousand",lang);
5157 if (tm.tm_year > 100) {
5158 if (!res) {
5159 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5160 res = wait_file(chan,ints,nextmsg,lang);
5161 if (!res) {
5162 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5163 res = wait_file(chan,ints,nextmsg,lang);
5167 if (!res) {
5168 res = wait_file(chan,ints, "digits/year",lang);
5170 } else {
5171 if (tm.tm_year < 1) {
5172 /* I'm not going to handle 1900 and prior */
5173 /* We'll just be silent on the year, instead of bombing out. */
5174 } else {
5175 res = wait_file(chan,ints, "digits/1",lang);
5176 if (!res) {
5177 res = wait_file(chan,ints, "digits/9",lang);
5179 if (!res) {
5180 if (tm.tm_year <= 9) {
5181 /* 1901 - 1909 */
5182 res = wait_file(chan,ints, "digits/0",lang);
5183 if (!res) {
5184 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5185 res = wait_file(chan,ints,nextmsg,lang);
5187 } else {
5188 /* 1910 - 1999 */
5189 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5190 res = wait_file(chan,ints,nextmsg,lang);
5191 if (!res) {
5192 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5193 res = wait_file(chan,ints,nextmsg,lang);
5198 if (!res) {
5199 res = wait_file(chan,ints, "digits/year",lang);
5202 break;
5203 case 'I':
5204 case 'l':
5205 /* 12-Hour */
5206 if (tm.tm_hour == 0)
5207 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5208 else if (tm.tm_hour > 12)
5209 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5210 else
5211 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5212 res = wait_file(chan,ints,nextmsg,lang);
5213 if (!res) {
5214 res = wait_file(chan,ints, "digits/oclock",lang);
5216 break;
5217 case 'H':
5218 case 'k':
5219 /* 24-Hour */
5220 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5221 if (tm.tm_hour < 10) {
5222 res = wait_file(chan, ints, "digits/0", lang);
5224 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5225 res = wait_file(chan,ints,nextmsg,lang);
5226 } else {
5227 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5228 res = wait_file(chan,ints,nextmsg,lang);
5229 if (!res) {
5230 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5231 res = wait_file(chan,ints,nextmsg,lang);
5234 if (!res) {
5235 res = wait_file(chan,ints, "digits/oclock",lang);
5237 break;
5238 case 'M':
5239 /* Minute */
5240 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5241 if (tm.tm_min < 10) {
5242 res = wait_file(chan, ints, "digits/0", lang);
5244 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5245 res = wait_file(chan,ints,nextmsg,lang);
5246 } else {
5247 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5248 res = wait_file(chan,ints,nextmsg,lang);
5249 if (!res) {
5250 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5251 res = wait_file(chan,ints,nextmsg,lang);
5254 if (!res) {
5255 res = wait_file(chan,ints, "digits/minute",lang);
5257 break;
5258 case 'P':
5259 case 'p':
5260 /* AM/PM */
5261 if (tm.tm_hour > 11)
5262 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5263 else
5264 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5265 res = wait_file(chan,ints,nextmsg,lang);
5266 break;
5267 case 'Q':
5268 /* Shorthand for "Today", "Yesterday", or ABdY */
5269 /* XXX As emphasized elsewhere, this should the native way in your
5270 * language to say the date, with changes in what you say, depending
5271 * upon how recent the date is. XXX */
5273 struct timeval now;
5274 struct tm tmnow;
5275 time_t beg_today, tt;
5277 gettimeofday(&now,NULL);
5278 tt = now.tv_sec;
5279 ast_localtime(&tt,&tmnow,timezone);
5280 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5281 /* In any case, it saves not having to do ast_mktime() */
5282 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5283 if (beg_today < time) {
5284 /* Today */
5285 res = wait_file(chan,ints, "digits/today",lang);
5286 } else if (beg_today - 86400 < time) {
5287 /* Yesterday */
5288 res = wait_file(chan,ints, "digits/yesterday",lang);
5289 } else {
5290 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5293 break;
5294 case 'q':
5295 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5296 /* XXX As emphasized elsewhere, this should the native way in your
5297 * language to say the date, with changes in what you say, depending
5298 * upon how recent the date is. XXX */
5300 struct timeval now;
5301 struct tm tmnow;
5302 time_t beg_today, tt;
5304 gettimeofday(&now,NULL);
5305 tt = now.tv_sec;
5306 ast_localtime(&tt,&tmnow,timezone);
5307 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5308 /* In any case, it saves not having to do ast_mktime() */
5309 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5310 if (beg_today < time) {
5311 /* Today */
5312 } else if ((beg_today - 86400) < time) {
5313 /* Yesterday */
5314 res = wait_file(chan,ints, "digits/yesterday",lang);
5315 } else if (beg_today - 86400 * 6 < time) {
5316 /* Within the last week */
5317 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5318 } else {
5319 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5322 break;
5323 case 'R':
5324 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HM", timezone);
5325 break;
5326 case 'S':
5327 /* Seconds */
5328 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5329 if (tm.tm_sec < 10) {
5330 res = wait_file(chan, ints, "digits/0", lang);
5332 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5333 res = wait_file(chan,ints,nextmsg,lang);
5334 } else {
5335 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5336 res = wait_file(chan,ints,nextmsg,lang);
5337 if (!res) {
5338 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5339 res = wait_file(chan,ints,nextmsg,lang);
5342 if (!res) {
5343 res = wait_file(chan,ints, "digits/second",lang);
5345 break;
5346 case 'T':
5347 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5348 break;
5349 case ' ':
5350 case ' ':
5351 /* Just ignore spaces and tabs */
5352 break;
5353 default:
5354 /* Unknown character */
5355 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5357 /* Jump out on DTMF */
5358 if (res) {
5359 break;
5362 return res;
5365 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5367 if (!strcasecmp(lang, "en") ) { /* English syntax */
5368 return(ast_say_time_en(chan, t, ints, lang));
5369 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5370 return(ast_say_time_de(chan, t, ints, lang));
5371 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5372 return(ast_say_time_fr(chan, t, ints, lang));
5373 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5374 return(ast_say_time_nl(chan, t, ints, lang));
5375 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5376 return(ast_say_time_pt(chan, t, ints, lang));
5377 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5378 return(ast_say_time_tw(chan, t, ints, lang));
5379 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5380 return(ast_say_time_gr(chan, t, ints, lang));
5383 /* Default to English */
5384 return(ast_say_time_en(chan, t, ints, lang));
5387 /* English syntax */
5388 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5390 struct tm tm;
5391 int res = 0;
5392 int hour, pm=0;
5393 localtime_r(&t,&tm);
5394 hour = tm.tm_hour;
5395 if (!hour)
5396 hour = 12;
5397 else if (hour == 12)
5398 pm = 1;
5399 else if (hour > 12) {
5400 hour -= 12;
5401 pm = 1;
5403 if (!res)
5404 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5406 if (tm.tm_min > 9) {
5407 if (!res)
5408 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5409 } else if (tm.tm_min) {
5410 if (!res)
5411 res = ast_streamfile(chan, "digits/oh", lang);
5412 if (!res)
5413 res = ast_waitstream(chan, ints);
5414 if (!res)
5415 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5416 } else {
5417 if (!res)
5418 res = ast_streamfile(chan, "digits/oclock", lang);
5419 if (!res)
5420 res = ast_waitstream(chan, ints);
5422 if (pm) {
5423 if (!res)
5424 res = ast_streamfile(chan, "digits/p-m", lang);
5425 } else {
5426 if (!res)
5427 res = ast_streamfile(chan, "digits/a-m", lang);
5429 if (!res)
5430 res = ast_waitstream(chan, ints);
5431 return res;
5434 /* German syntax */
5435 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5437 struct tm tm;
5438 int res = 0;
5439 localtime_r(&t,&tm);
5440 if (!res)
5441 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5442 if (!res)
5443 res = ast_streamfile(chan, "digits/oclock", lang);
5444 if (!res)
5445 res = ast_waitstream(chan, ints);
5446 if (!res)
5447 if (tm.tm_min > 0)
5448 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5449 return res;
5452 /* French syntax */
5453 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5455 struct tm tm;
5456 int res = 0;
5457 localtime_r(&t,&tm);
5459 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5460 if (!res)
5461 res = ast_streamfile(chan, "digits/oclock", lang);
5462 if (tm.tm_min) {
5463 if (!res)
5464 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5466 return res;
5469 /* Dutch syntax */
5470 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5472 struct tm tm;
5473 int res = 0;
5474 localtime_r(&t,&tm);
5475 if (!res)
5476 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5477 if (!res)
5478 res = ast_streamfile(chan, "digits/nl-uur", lang);
5479 if (!res)
5480 res = ast_waitstream(chan, ints);
5481 if (!res)
5482 if (tm.tm_min > 0)
5483 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5484 return res;
5487 /* Portuguese syntax */
5488 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5490 struct tm tm;
5491 int res = 0;
5492 int hour;
5493 localtime_r(&t,&tm);
5494 hour = tm.tm_hour;
5495 if (!res)
5496 res = ast_say_number(chan, hour, ints, lang, "f");
5497 if (tm.tm_min) {
5498 if (!res)
5499 res = wait_file(chan, ints, "digits/pt-e", lang);
5500 if (!res)
5501 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5502 } else {
5503 if (!res)
5504 res = wait_file(chan, ints, "digits/pt-hora", lang);
5505 if (tm.tm_hour != 1)
5506 if (!res)
5507 res = wait_file(chan, ints, "digits/pt-sss", lang);
5509 if (!res)
5510 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5511 return res;
5514 /* Taiwanese / Chinese syntax */
5515 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5517 struct tm tm;
5518 int res = 0;
5519 int hour, pm=0;
5520 localtime_r(&t,&tm);
5521 hour = tm.tm_hour;
5522 if (!hour)
5523 hour = 12;
5524 else if (hour == 12)
5525 pm = 1;
5526 else if (hour > 12) {
5527 hour -= 12;
5528 pm = 1;
5530 if (pm) {
5531 if (!res)
5532 res = ast_streamfile(chan, "digits/p-m", lang);
5533 } else {
5534 if (!res)
5535 res = ast_streamfile(chan, "digits/a-m", lang);
5537 if (!res)
5538 res = ast_waitstream(chan, ints);
5539 if (!res)
5540 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5541 if (!res)
5542 res = ast_streamfile(chan, "digits/oclock", lang);
5543 if (!res)
5544 res = ast_waitstream(chan, ints);
5545 if (!res)
5546 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5547 if (!res)
5548 res = ast_streamfile(chan, "digits/minute", lang);
5549 if (!res)
5550 res = ast_waitstream(chan, ints);
5551 return res;
5554 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5556 if (!strcasecmp(lang, "en") ) { /* English syntax */
5557 return(ast_say_datetime_en(chan, t, ints, lang));
5558 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5559 return(ast_say_datetime_de(chan, t, ints, lang));
5560 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5561 return(ast_say_datetime_fr(chan, t, ints, lang));
5562 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5563 return(ast_say_datetime_nl(chan, t, ints, lang));
5564 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5565 return(ast_say_datetime_pt(chan, t, ints, lang));
5566 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5567 return(ast_say_datetime_tw(chan, t, ints, lang));
5568 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5569 return(ast_say_datetime_gr(chan, t, ints, lang));
5572 /* Default to English */
5573 return(ast_say_datetime_en(chan, t, ints, lang));
5576 /* English syntax */
5577 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5579 struct tm tm;
5580 char fn[256];
5581 int res = 0;
5582 int hour, pm=0;
5583 localtime_r(&t,&tm);
5584 if (!res) {
5585 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5586 res = ast_streamfile(chan, fn, lang);
5587 if (!res)
5588 res = ast_waitstream(chan, ints);
5590 if (!res) {
5591 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5592 res = ast_streamfile(chan, fn, lang);
5593 if (!res)
5594 res = ast_waitstream(chan, ints);
5596 if (!res)
5597 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5599 hour = tm.tm_hour;
5600 if (!hour)
5601 hour = 12;
5602 else if (hour == 12)
5603 pm = 1;
5604 else if (hour > 12) {
5605 hour -= 12;
5606 pm = 1;
5608 if (!res)
5609 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5611 if (tm.tm_min > 9) {
5612 if (!res)
5613 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5614 } else if (tm.tm_min) {
5615 if (!res)
5616 res = ast_streamfile(chan, "digits/oh", lang);
5617 if (!res)
5618 res = ast_waitstream(chan, ints);
5619 if (!res)
5620 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5621 } else {
5622 if (!res)
5623 res = ast_streamfile(chan, "digits/oclock", lang);
5624 if (!res)
5625 res = ast_waitstream(chan, ints);
5627 if (pm) {
5628 if (!res)
5629 res = ast_streamfile(chan, "digits/p-m", lang);
5630 } else {
5631 if (!res)
5632 res = ast_streamfile(chan, "digits/a-m", lang);
5634 if (!res)
5635 res = ast_waitstream(chan, ints);
5636 if (!res)
5637 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5638 return res;
5641 /* German syntax */
5642 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5644 struct tm tm;
5645 int res = 0;
5646 localtime_r(&t,&tm);
5647 res = ast_say_date(chan, t, ints, lang);
5648 if (!res)
5649 ast_say_time(chan, t, ints, lang);
5650 return res;
5654 /* French syntax */
5655 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5657 struct tm tm;
5658 char fn[256];
5659 int res = 0;
5660 localtime_r(&t,&tm);
5662 if (!res)
5663 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5665 if (!res) {
5666 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5667 res = ast_streamfile(chan, fn, lang);
5668 if (!res)
5669 res = ast_waitstream(chan, ints);
5671 if (!res) {
5672 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5673 res = ast_streamfile(chan, fn, lang);
5674 if (!res)
5675 res = ast_waitstream(chan, ints);
5678 if (!res)
5679 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5680 if (!res)
5681 res = ast_streamfile(chan, "digits/oclock", lang);
5682 if (tm.tm_min > 0) {
5683 if (!res)
5684 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5686 if (!res)
5687 res = ast_waitstream(chan, ints);
5688 if (!res)
5689 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5690 return res;
5693 /* Dutch syntax */
5694 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5696 struct tm tm;
5697 int res = 0;
5698 localtime_r(&t,&tm);
5699 res = ast_say_date(chan, t, ints, lang);
5700 if (!res) {
5701 res = ast_streamfile(chan, "digits/nl-om", lang);
5702 if (!res)
5703 res = ast_waitstream(chan, ints);
5705 if (!res)
5706 ast_say_time(chan, t, ints, lang);
5707 return res;
5710 /* Portuguese syntax */
5711 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5713 struct tm tm;
5714 char fn[256];
5715 int res = 0;
5716 int hour, pm=0;
5717 localtime_r(&t,&tm);
5718 if (!res) {
5719 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5720 res = ast_streamfile(chan, fn, lang);
5721 if (!res)
5722 res = ast_waitstream(chan, ints);
5724 if (!res) {
5725 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5726 res = ast_streamfile(chan, fn, lang);
5727 if (!res)
5728 res = ast_waitstream(chan, ints);
5730 if (!res)
5731 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5733 hour = tm.tm_hour;
5734 if (!hour)
5735 hour = 12;
5736 else if (hour == 12)
5737 pm = 1;
5738 else if (hour > 12) {
5739 hour -= 12;
5740 pm = 1;
5742 if (!res)
5743 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5745 if (tm.tm_min > 9) {
5746 if (!res)
5747 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5748 } else if (tm.tm_min) {
5749 if (!res)
5750 res = ast_streamfile(chan, "digits/oh", lang);
5751 if (!res)
5752 res = ast_waitstream(chan, ints);
5753 if (!res)
5754 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5755 } else {
5756 if (!res)
5757 res = ast_streamfile(chan, "digits/oclock", lang);
5758 if (!res)
5759 res = ast_waitstream(chan, ints);
5761 if (pm) {
5762 if (!res)
5763 res = ast_streamfile(chan, "digits/p-m", lang);
5764 } else {
5765 if (!res)
5766 res = ast_streamfile(chan, "digits/a-m", lang);
5768 if (!res)
5769 res = ast_waitstream(chan, ints);
5770 if (!res)
5771 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5772 return res;
5775 /* Taiwanese / Chinese syntax */
5776 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5778 struct tm tm;
5779 char fn[256];
5780 int res = 0;
5781 int hour, pm=0;
5782 localtime_r(&t,&tm);
5783 if (!res)
5784 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5785 if (!res) {
5786 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5787 res = ast_streamfile(chan, fn, lang);
5788 if (!res)
5789 res = ast_waitstream(chan, ints);
5791 if (!res)
5792 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5793 if (!res) {
5794 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5795 res = ast_streamfile(chan, fn, lang);
5796 if (!res)
5797 res = ast_waitstream(chan, ints);
5800 hour = tm.tm_hour;
5801 if (!hour)
5802 hour = 12;
5803 else if (hour == 12)
5804 pm = 1;
5805 else if (hour > 12) {
5806 hour -= 12;
5807 pm = 1;
5809 if (pm) {
5810 if (!res)
5811 res = ast_streamfile(chan, "digits/p-m", lang);
5812 } else {
5813 if (!res)
5814 res = ast_streamfile(chan, "digits/a-m", lang);
5816 if (!res)
5817 res = ast_waitstream(chan, ints);
5818 if (!res)
5819 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5820 if (!res)
5821 res = ast_streamfile(chan, "digits/oclock", lang);
5822 if (!res)
5823 res = ast_waitstream(chan, ints);
5824 if (!res)
5825 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5826 if (!res)
5827 res = ast_streamfile(chan, "digits/minute", lang);
5828 if (!res)
5829 res = ast_waitstream(chan, ints);
5830 return res;
5833 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5835 if (!strcasecmp(lang, "en") ) { /* English syntax */
5836 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5837 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5838 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
5839 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5840 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
5843 /* Default to English */
5844 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5847 /* English syntax */
5848 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5850 int res=0;
5851 time_t nowt;
5852 int daydiff;
5853 struct tm tm;
5854 struct tm now;
5855 char fn[256];
5857 time(&nowt);
5859 localtime_r(&t,&tm);
5860 localtime_r(&nowt,&now);
5861 daydiff = now.tm_yday - tm.tm_yday;
5862 if ((daydiff < 0) || (daydiff > 6)) {
5863 /* Day of month and month */
5864 if (!res) {
5865 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5866 res = ast_streamfile(chan, fn, lang);
5867 if (!res)
5868 res = ast_waitstream(chan, ints);
5870 if (!res)
5871 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5873 } else if (daydiff) {
5874 /* Just what day of the week */
5875 if (!res) {
5876 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5877 res = ast_streamfile(chan, fn, lang);
5878 if (!res)
5879 res = ast_waitstream(chan, ints);
5881 } /* Otherwise, it was today */
5882 if (!res)
5883 res = ast_say_time(chan, t, ints, lang);
5884 return res;
5887 /* French syntax */
5888 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5890 int res=0;
5891 time_t nowt;
5892 int daydiff;
5893 struct tm tm;
5894 struct tm now;
5895 char fn[256];
5897 time(&nowt);
5899 localtime_r(&t,&tm);
5900 localtime_r(&nowt,&now);
5901 daydiff = now.tm_yday - tm.tm_yday;
5902 if ((daydiff < 0) || (daydiff > 6)) {
5903 /* Day of month and month */
5904 if (!res) {
5905 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5906 res = ast_streamfile(chan, fn, lang);
5907 if (!res)
5908 res = ast_waitstream(chan, ints);
5910 if (!res)
5911 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5913 } else if (daydiff) {
5914 /* Just what day of the week */
5915 if (!res) {
5916 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5917 res = ast_streamfile(chan, fn, lang);
5918 if (!res)
5919 res = ast_waitstream(chan, ints);
5921 } /* Otherwise, it was today */
5922 if (!res)
5923 res = ast_say_time(chan, t, ints, lang);
5924 return res;
5927 /* Portuguese syntax */
5928 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5930 int res=0;
5931 time_t nowt;
5932 int daydiff;
5933 struct tm tm;
5934 struct tm now;
5935 char fn[256];
5937 time(&nowt);
5939 localtime_r(&t,&tm);
5940 localtime_r(&nowt,&now);
5941 daydiff = now.tm_yday - tm.tm_yday;
5942 if ((daydiff < 0) || (daydiff > 6)) {
5943 /* Day of month and month */
5944 if (!res)
5945 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5946 if (!res)
5947 res = wait_file(chan, ints, "digits/pt-de", lang);
5948 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5949 if (!res)
5950 res = wait_file(chan, ints, fn, lang);
5952 } else if (daydiff) {
5953 /* Just what day of the week */
5954 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5955 if (!res)
5956 res = wait_file(chan, ints, fn, lang);
5957 } /* Otherwise, it was today */
5958 snprintf(fn, sizeof(fn), "digits/pt-ah");
5959 if (!res)
5960 res = wait_file(chan, ints, fn, lang);
5961 if (tm.tm_hour != 1)
5962 if (!res)
5963 res = wait_file(chan, ints, "digits/pt-sss", lang);
5964 if (!res)
5965 res = ast_say_time(chan, t, ints, lang);
5966 return res;
5970 /*********************************** GREEK SUPPORT ***************************************/
5974 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
5976 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
5977 int tmp;
5978 int left;
5979 int res;
5980 char fn[256] = "";
5982 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
5983 if (num < 5) {
5984 snprintf(fn, sizeof(fn), "digits/female-%d", num);
5985 res = wait_file(chan, ints, fn, lang);
5986 } else if (num < 13) {
5987 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
5988 } else if (num <100 ) {
5989 tmp = (num/10) * 10;
5990 left = num - tmp;
5991 snprintf(fn, sizeof(fn), "digits/%d", tmp);
5992 res = ast_streamfile(chan, fn, lang);
5993 if (!res)
5994 res = ast_waitstream(chan, ints);
5995 if (left)
5996 gr_say_number_female(left, chan, ints, lang);
5998 } else {
5999 return -1;
6001 return res;
6007 * A list of the files that you need to create
6008 -> digits/xilia = "xilia"
6009 -> digits/myrio = "ekatomyrio"
6010 -> digits/thousands = "xiliades"
6011 -> digits/millions = "ektatomyria"
6012 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6013 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6014 e,g 80 = "ogdonta"
6015 Here we must note that we use digits/tens/100 to utter "ekato"
6016 and digits/hundred-100 to utter "ekaton"
6017 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6018 "terakosia". Here again we use hundreds/1000 for "xilia"
6019 and digits/thousnds for "xiliades"
6022 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6024 int res = 0;
6025 char fn[256] = "";
6026 int i=0;
6029 if (!num) {
6030 snprintf(fn, sizeof(fn), "digits/0");
6031 res = ast_streamfile(chan, fn, chan->language);
6032 if (!res)
6033 return ast_waitstream(chan, ints);
6036 while(!res && num ) {
6037 i++;
6038 if (num < 13) {
6039 snprintf(fn, sizeof(fn), "digits/%d", num);
6040 num = 0;
6041 } else if (num <= 100) {
6042 /* 13 < num <= 100 */
6043 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6044 num -= ((num / 10) * 10);
6045 } else if (num < 200) {
6046 /* 100 < num < 200 */
6047 snprintf(fn, sizeof(fn), "digits/hundred-100");
6048 num -= ((num / 100) * 100);
6049 }else if (num < 1000) {
6050 /* 200 < num < 1000 */
6051 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6052 num -= ((num / 100) * 100);
6053 }else if (num < 2000){
6054 snprintf(fn, sizeof(fn), "digits/xilia");
6055 num -= ((num / 1000) * 1000);
6057 else {
6058 /* num > 1000 */
6059 if (num < 1000000) {
6060 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6061 if (res)
6062 return res;
6063 num = num % 1000;
6064 snprintf(fn, sizeof(fn), "digits/thousands");
6065 } else {
6066 if (num < 1000000000) { /* 1,000,000,000 */
6067 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6068 if (res)
6069 return res;
6070 num = num % 1000000;
6071 snprintf(fn, sizeof(fn), "digits/millions");
6072 } else {
6073 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6074 res = -1;
6078 if (!res) {
6079 if(!ast_streamfile(chan, fn, language)) {
6080 if ((audiofd > -1) && (ctrlfd > -1))
6081 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6082 else
6083 res = ast_waitstream(chan, ints);
6085 ast_stopstream(chan);
6088 return res;
6093 * The format is weekday - day - month -year
6095 * A list of the files that you need to create
6096 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6097 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6098 Attention the months are in
6099 "gekinh klhsh"
6103 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6105 struct tm tm;
6107 char fn[256];
6108 int res = 0;
6111 ast_localtime(&t,&tm,NULL);
6112 /* W E E K - D A Y */
6113 if (!res) {
6114 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6115 res = ast_streamfile(chan, fn, lang);
6116 if (!res)
6117 res = ast_waitstream(chan, ints);
6119 /* D A Y */
6120 if (!res) {
6121 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6123 /* M O N T H */
6124 if (!res) {
6125 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6126 res = ast_streamfile(chan, fn, lang);
6127 if (!res)
6128 res = ast_waitstream(chan, ints);
6130 /* Y E A R */
6131 if (!res)
6132 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6133 return res;
6138 /* A list of the files that you need to create
6139 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6140 * digits/kai : "KAI"
6141 * didgits : "h wra"
6142 * digits/p-m : "meta meshmbrias"
6143 * digits/a-m : "pro meshmbrias"
6146 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6149 struct tm tm;
6150 int res = 0;
6151 int hour, pm=0;
6153 localtime_r(&t,&tm);
6154 hour = tm.tm_hour;
6156 if (!hour)
6157 hour = 12;
6158 else if (hour == 12)
6159 pm = 1;
6160 else if (hour > 12) {
6161 hour -= 12;
6162 pm = 1;
6165 res = gr_say_number_female(hour, chan, ints, lang);
6166 if (tm.tm_min) {
6167 if (!res)
6168 res = ast_streamfile(chan, "digits/kai", lang);
6169 if (!res)
6170 res = ast_waitstream(chan, ints);
6171 if (!res)
6172 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6173 } else {
6174 if (!res)
6175 res = ast_streamfile(chan, "digits/hwra", lang);
6176 if (!res)
6177 res = ast_waitstream(chan, ints);
6179 if (pm) {
6180 if (!res)
6181 res = ast_streamfile(chan, "digits/p-m", lang);
6182 } else {
6183 if (!res)
6184 res = ast_streamfile(chan, "digits/a-m", lang);
6186 if (!res)
6187 res = ast_waitstream(chan, ints);
6188 return res;
6193 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6195 struct tm tm;
6196 char fn[256];
6197 int res = 0;
6198 localtime_r(&t,&tm);
6201 /* W E E K - D A Y */
6202 if (!res) {
6203 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6204 res = ast_streamfile(chan, fn, lang);
6205 if (!res)
6206 res = ast_waitstream(chan, ints);
6208 /* D A Y */
6209 if (!res) {
6210 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6212 /* M O N T H */
6213 if (!res) {
6214 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6215 res = ast_streamfile(chan, fn, lang);
6216 if (!res)
6217 res = ast_waitstream(chan, ints);
6220 res = ast_say_time_gr(chan, t, ints, lang);
6221 return res;
6224 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)
6227 struct tm tm;
6228 int res=0, offset, sndoffset;
6229 char sndfile[256], nextmsg[256];
6231 if (!format)
6232 format = "AdBY 'digits/at' IMp";
6234 ast_localtime(&time,&tm,timezone);
6236 for (offset=0 ; format[offset] != '\0' ; offset++) {
6237 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6238 switch (format[offset]) {
6239 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6240 case '\'':
6241 /* Literal name of a sound file */
6242 sndoffset=0;
6243 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6244 sndfile[sndoffset] = format[offset];
6245 sndfile[sndoffset] = '\0';
6246 res = wait_file(chan,ints,sndfile,lang);
6247 break;
6248 case 'A':
6249 case 'a':
6250 /* Sunday - Saturday */
6251 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6252 res = wait_file(chan,ints,nextmsg,lang);
6253 break;
6254 case 'B':
6255 case 'b':
6256 case 'h':
6257 /* January - December */
6258 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6259 res = wait_file(chan,ints,nextmsg,lang);
6260 break;
6261 case 'd':
6262 case 'e':
6263 /* first - thirtyfirst */
6264 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6265 break;
6266 case 'Y':
6267 /* Year */
6269 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6270 break;
6271 case 'I':
6272 case 'l':
6273 /* 12-Hour */
6274 if (tm.tm_hour == 0)
6275 gr_say_number_female(12, chan, ints, lang);
6276 else if (tm.tm_hour > 12)
6277 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6278 else
6279 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6280 break;
6281 case 'H':
6282 case 'k':
6283 /* 24-Hour */
6284 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6285 break;
6286 case 'M':
6287 /* Minute */
6288 if (tm.tm_min) {
6289 if (!res)
6290 res = ast_streamfile(chan, "digits/kai", lang);
6291 if (!res)
6292 res = ast_waitstream(chan, ints);
6293 if (!res)
6294 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6295 } else {
6296 if (!res)
6297 res = ast_streamfile(chan, "digits/oclock", lang);
6298 if (!res)
6299 res = ast_waitstream(chan, ints);
6301 break;
6302 case 'P':
6303 case 'p':
6304 /* AM/PM */
6305 if (tm.tm_hour > 11)
6306 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6307 else
6308 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6309 res = wait_file(chan,ints,nextmsg,lang);
6310 break;
6311 case 'Q':
6312 /* Shorthand for "Today", "Yesterday", or ABdY */
6313 /* XXX As emphasized elsewhere, this should the native way in your
6314 * language to say the date, with changes in what you say, depending
6315 * upon how recent the date is. XXX */
6317 struct timeval now;
6318 struct tm tmnow;
6319 time_t beg_today, tt;
6321 gettimeofday(&now,NULL);
6322 tt = now.tv_sec;
6323 ast_localtime(&tt,&tmnow,timezone);
6324 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6325 /* In any case, it saves not having to do ast_mktime() */
6326 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6327 if (beg_today < time) {
6328 /* Today */
6329 res = wait_file(chan,ints, "digits/today",lang);
6330 } else if (beg_today - 86400 < time) {
6331 /* Yesterday */
6332 res = wait_file(chan,ints, "digits/yesterday",lang);
6333 } else {
6334 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6337 break;
6338 case 'q':
6339 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6340 /* XXX As emphasized elsewhere, this should the native way in your
6341 * language to say the date, with changes in what you say, depending
6342 * upon how recent the date is. XXX */
6344 struct timeval now;
6345 struct tm tmnow;
6346 time_t beg_today, tt;
6348 gettimeofday(&now,NULL);
6349 tt = now.tv_sec;
6350 ast_localtime(&tt,&tmnow,timezone);
6351 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6352 /* In any case, it saves not having to do ast_mktime() */
6353 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6354 if (beg_today < time) {
6355 /* Today */
6356 } else if ((beg_today - 86400) < time) {
6357 /* Yesterday */
6358 res = wait_file(chan,ints, "digits/yesterday",lang);
6359 } else if (beg_today - 86400 * 6 < time) {
6360 /* Within the last week */
6361 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6362 } else {
6363 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6366 break;
6367 case 'R':
6368 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6369 break;
6370 case 'S':
6371 /* Seconds */
6372 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6373 res = wait_file(chan,ints,nextmsg,lang);
6374 if (!res)
6375 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6376 if (!res)
6377 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6378 res = wait_file(chan,ints,nextmsg,lang);
6379 break;
6380 case 'T':
6381 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6382 break;
6383 case ' ':
6384 case ' ':
6385 /* Just ignore spaces and tabs */
6386 break;
6387 default:
6388 /* Unknown character */
6389 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6391 /* Jump out on DTMF */
6392 if (res) {
6393 break;
6396 return res;
6400 * remap the 'say' functions to use those in this file
6402 static void __attribute__((constructor)) __say_init(void)
6404 ast_say_number_full = say_number_full;
6405 ast_say_enumeration_full = say_enumeration_full;
6406 ast_say_digit_str_full = say_digit_str_full;
6407 ast_say_character_str_full = say_character_str_full;
6408 ast_say_phonetic_str_full = say_phonetic_str_full;
6409 ast_say_datetime = say_datetime;
6410 ast_say_time = say_time;
6411 ast_say_date = say_date;
6412 ast_say_datetime_from_now = say_datetime_from_now;
6413 ast_say_date_with_format = say_date_with_format;