use the proper type for storing group number bits so that if someone specifies 'group...
[asterisk-bristuff.git] / main / say.c
blob2c3937c761950acb8d9a8083de1c12984fe1f986
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>
28 * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
29 * Next Generation Networks (NGN).
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <sys/types.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <netinet/in.h>
40 #include <time.h>
41 #include <ctype.h>
42 #include <math.h>
43 #include <stdio.h>
45 #ifdef SOLARIS
46 #include <iso/limits_iso.h>
47 #endif
49 #include "asterisk/file.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/options.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
58 /* Forward declaration */
59 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
62 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
64 const char *fn;
65 char fnbuf[256];
66 char ltr;
67 int num = 0;
68 int res = 0;
70 while (str[num] && !res) {
71 fn = NULL;
72 switch (str[num]) {
73 case ('*'):
74 fn = "digits/star";
75 break;
76 case ('#'):
77 fn = "digits/pound";
78 break;
79 case ('!'):
80 fn = "letters/exclaimation-point";
81 break;
82 case ('@'):
83 fn = "letters/at";
84 break;
85 case ('$'):
86 fn = "letters/dollar";
87 break;
88 case ('-'):
89 fn = "letters/dash";
90 break;
91 case ('.'):
92 fn = "letters/dot";
93 break;
94 case ('='):
95 fn = "letters/equals";
96 break;
97 case ('+'):
98 fn = "letters/plus";
99 break;
100 case ('/'):
101 fn = "letters/slash";
102 break;
103 case (' '):
104 fn = "letters/space";
105 break;
106 case ('0'):
107 case ('1'):
108 case ('2'):
109 case ('3'):
110 case ('4'):
111 case ('5'):
112 case ('6'):
113 case ('7'):
114 case ('8'):
115 case ('9'):
116 strcpy(fnbuf, "digits/X");
117 fnbuf[7] = str[num];
118 fn = fnbuf;
119 break;
120 default:
121 ltr = str[num];
122 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
123 strcpy(fnbuf, "letters/X");
124 fnbuf[8] = ltr;
125 fn = fnbuf;
127 res = ast_streamfile(chan, fn, lang);
128 if (!res) {
129 if ((audiofd > -1) && (ctrlfd > -1))
130 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
131 else
132 res = ast_waitstream(chan, ints);
134 ast_stopstream(chan);
135 num++;
138 return res;
141 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
143 const char *fn;
144 char fnbuf[256];
145 char ltr;
146 int num = 0;
147 int res = 0;
149 while (str[num] && !res) {
150 fn = NULL;
151 switch (str[num]) {
152 case ('*'):
153 fn = "digits/star";
154 break;
155 case ('#'):
156 fn = "digits/pound";
157 break;
158 case ('!'):
159 fn = "letters/exclaimation-point";
160 break;
161 case ('@'):
162 fn = "letters/at";
163 break;
164 case ('$'):
165 fn = "letters/dollar";
166 break;
167 case ('-'):
168 fn = "letters/dash";
169 break;
170 case ('.'):
171 fn = "letters/dot";
172 break;
173 case ('='):
174 fn = "letters/equals";
175 break;
176 case ('+'):
177 fn = "letters/plus";
178 break;
179 case ('/'):
180 fn = "letters/slash";
181 break;
182 case (' '):
183 fn = "letters/space";
184 break;
185 case ('0'):
186 case ('1'):
187 case ('2'):
188 case ('3'):
189 case ('4'):
190 case ('5'):
191 case ('6'):
192 case ('7'):
193 case ('8'):
194 strcpy(fnbuf, "digits/X");
195 fnbuf[7] = str[num];
196 fn = fnbuf;
197 break;
198 default: /* '9' falls here... */
199 ltr = str[num];
200 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
201 strcpy(fnbuf, "phonetic/X_p");
202 fnbuf[9] = ltr;
203 fn = fnbuf;
205 res = ast_streamfile(chan, fn, lang);
206 if (!res) {
207 if ((audiofd > -1) && (ctrlfd > -1))
208 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
209 else
210 res = ast_waitstream(chan, ints);
212 ast_stopstream(chan);
213 num++;
216 return res;
219 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
221 const char *fn;
222 char fnbuf[256];
223 int num = 0;
224 int res = 0;
226 while (str[num] && !res) {
227 fn = NULL;
228 switch (str[num]) {
229 case ('*'):
230 fn = "digits/star";
231 break;
232 case ('#'):
233 fn = "digits/pound";
234 break;
235 case ('-'):
236 fn = "digits/minus";
237 break;
238 case '0':
239 case '1':
240 case '2':
241 case '3':
242 case '4':
243 case '5':
244 case '6':
245 case '7':
246 case '8':
247 case '9':
248 strcpy(fnbuf, "digits/X");
249 fnbuf[7] = str[num];
250 fn = fnbuf;
251 break;
253 if (fn) {
254 res = ast_streamfile(chan, fn, lang);
255 if (!res) {
256 if ((audiofd > -1) && (ctrlfd > -1))
257 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
258 else
259 res = ast_waitstream(chan, ints);
261 ast_stopstream(chan);
263 num++;
266 return res;
269 /* Forward declarations */
270 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
271 \note Not really language codes.
272 For these language codes, Asterisk will change the syntax when
273 saying numbers (and in some cases dates and voicemail messages
274 as well)
275 \arg \b da - Danish
276 \arg \b de - German
277 \arg \b en - English (US)
278 \arg \b en_GB - English (British)
279 \arg \b es - Spanish, Mexican
280 \arg \b fr - French
281 \arg \b he - Hebrew
282 \arg \b it - Italian
283 \arg \b nl - Dutch
284 \arg \b no - Norwegian
285 \arg \b pl - Polish
286 \arg \b pt - Portuguese
287 \arg \b pt_BR - Portuguese (Brazil)
288 \arg \b se - Swedish
289 \arg \b tw - Taiwanese / Chinese
290 \arg \b ru - Russian
291 \arg \b ge - Georgian
293 \par Gender:
294 For Some languages the numbers differ for gender and plural.
295 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
296 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
297 use the option argument 'p' for plural enumerations like in German
299 Date/Time functions currently have less languages supported than saynumber().
301 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
303 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
305 \par Portuguese
306 Portuguese sound files needed for Time/Date functions:
307 pt-ah
308 pt-ao
309 pt-de
310 pt-e
311 pt-ora
312 pt-meianoite
313 pt-meiodia
314 pt-sss
316 \par Spanish
317 Spanish sound files needed for Time/Date functions:
318 es-de
319 es-el
321 \par Italian
322 Italian sound files needed for Time/Date functions:
323 ore-una
324 ore-mezzanotte
328 /* Forward declarations of language specific variants of ast_say_number_full */
329 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
330 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);
331 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);
332 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);
333 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
334 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);
335 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);
336 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);
337 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
338 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
339 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);
340 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);
341 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);
342 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);
343 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
344 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
345 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);
346 static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
348 /* Forward declarations of language specific variants of ast_say_enumeration_full */
349 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
350 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);
351 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);
353 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
354 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
355 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
356 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
357 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
358 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
359 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
360 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
361 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 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);
364 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);
365 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);
366 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);
367 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);
368 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);
369 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);
370 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);
371 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);
372 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);
373 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);
374 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);
376 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
377 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
378 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
379 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
380 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
381 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
382 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
383 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
384 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
386 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
387 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
389 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
390 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
392 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
399 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
403 int res;
404 if ((res = ast_streamfile(chan, file, lang)))
405 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
406 if (!res)
407 res = ast_waitstream(chan, ints);
408 return res;
411 /*! \brief ast_say_number_full: call language-specific functions */
412 /* Called from AGI */
413 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
415 if (!strcasecmp(language,"en") ) { /* English syntax */
416 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
417 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
418 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
419 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
420 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
421 } else if (!strcasecmp(language, "de") ) { /* German syntax */
422 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
423 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
424 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
425 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
426 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
427 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
428 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
429 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
430 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
431 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
432 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
433 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
434 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
435 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
436 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
437 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
438 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
439 } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
440 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
441 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
442 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
443 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
444 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
445 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
446 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
447 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
448 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
449 } else if (!strcasecmp(language, "ge") ) { /* Georgian syntax */
450 return(ast_say_number_full_ge(chan, num, ints, language, options, audiofd, ctrlfd));
453 /* Default to english */
454 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
457 /*! \brief ast_say_number_full_en: English syntax */
458 /* This is the default syntax, if no other syntax defined in this file is used */
459 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
461 int res = 0;
462 int playh = 0;
463 char fn[256] = "";
464 if (!num)
465 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
467 while(!res && (num || playh)) {
468 if (num < 0) {
469 snprintf(fn, sizeof(fn), "digits/minus");
470 if ( num > INT_MIN ) {
471 num = -num;
472 } else {
473 num = 0;
475 } else if (playh) {
476 snprintf(fn, sizeof(fn), "digits/hundred");
477 playh = 0;
478 } else if (num < 20) {
479 snprintf(fn, sizeof(fn), "digits/%d", num);
480 num = 0;
481 } else if (num < 100) {
482 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
483 num -= ((num / 10) * 10);
484 } else {
485 if (num < 1000){
486 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
487 playh++;
488 num -= ((num / 100) * 100);
489 } else {
490 if (num < 1000000) { /* 1,000,000 */
491 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
492 if (res)
493 return res;
494 num = num % 1000;
495 snprintf(fn, sizeof(fn), "digits/thousand");
496 } else {
497 if (num < 1000000000) { /* 1,000,000,000 */
498 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
499 if (res)
500 return res;
501 num = num % 1000000;
502 snprintf(fn, sizeof(fn), "digits/million");
503 } else {
504 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
505 res = -1;
510 if (!res) {
511 if(!ast_streamfile(chan, fn, language)) {
512 if ((audiofd > -1) && (ctrlfd > -1))
513 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
514 else
515 res = ast_waitstream(chan, ints);
517 ast_stopstream(chan);
520 return res;
523 static int exp10_int(int power)
525 int x, res= 1;
526 for (x=0;x<power;x++)
527 res *= 10;
528 return res;
531 /*! \brief ast_say_number_full_cz: Czech syntax */
532 /* files needed:
533 * 1m,2m - gender male
534 * 1w,2w - gender female
535 * 3,4,...,20
536 * 30,40,...,90
538 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
540 * for each number 10^(3n + 3) exist 3 files represented as:
541 * 1 tousand = jeden tisic = 1_E3
542 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
543 * 5,6,... tousands = pet,sest,... tisic = 5_E3
545 * million = _E6
546 * miliard = _E9
547 * etc...
549 * tousand, milion are gender male, so 1 and 2 is 1m 2m
550 * miliard is gender female, so 1 and 2 is 1w 2w
552 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)
554 int res = 0;
555 int playh = 0;
556 char fn[256] = "";
558 int hundered = 0;
559 int left = 0;
560 int length = 0;
562 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
563 if (!options)
564 options = "w";
566 if (!num)
567 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
569 while(!res && (num || playh)) {
570 if (num < 0) {
571 snprintf(fn, sizeof(fn), "digits/minus");
572 if ( num > INT_MIN ) {
573 num = -num;
574 } else {
575 num = 0;
577 } else if (num < 3 ) {
578 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
579 playh = 0;
580 num = 0;
581 } else if (num < 20) {
582 snprintf(fn, sizeof(fn), "digits/%d",num);
583 playh = 0;
584 num = 0;
585 } else if (num < 100) {
586 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
587 num -= ((num / 10) * 10);
588 } else if (num < 1000) {
589 hundered = num / 100;
590 if ( hundered == 1 ) {
591 snprintf(fn, sizeof(fn), "digits/1sto");
592 } else if ( hundered == 2 ) {
593 snprintf(fn, sizeof(fn), "digits/2ste");
594 } else {
595 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
596 if (res)
597 return res;
598 if (hundered == 3 || hundered == 4) {
599 snprintf(fn, sizeof(fn), "digits/sta");
600 } else if ( hundered > 4 ) {
601 snprintf(fn, sizeof(fn), "digits/set");
604 num -= (hundered * 100);
605 } else { /* num > 1000 */
606 length = (int)log10(num)+1;
607 while ( (length % 3 ) != 1 ) {
608 length--;
610 left = num / (exp10_int(length-1));
611 if ( left == 2 ) {
612 switch (length-1) {
613 case 9: options = "w"; /* 1,000,000,000 gender female */
614 break;
615 default : options = "m"; /* others are male */
618 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
619 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
620 if (res)
621 return res;
623 if ( left >= 5 ) { /* >= 5 have the same declesion */
624 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
625 } else if ( left >= 2 && left <= 4 ) {
626 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
627 } else { /* left == 1 */
628 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
630 num -= left * (exp10_int(length-1));
632 if (!res) {
633 if(!ast_streamfile(chan, fn, language)) {
634 if ((audiofd > -1) && (ctrlfd > -1)) {
635 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
636 } else {
637 res = ast_waitstream(chan, ints);
640 ast_stopstream(chan);
643 return res;
646 /*! \brief ast_say_number_full_da: Danish syntax */
647 /* New files:
648 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
650 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)
652 int res = 0;
653 int playh = 0;
654 int playa = 0;
655 int cn = 1; /* +1 = commune; -1 = neuter */
656 char fn[256] = "";
657 if (!num)
658 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
660 if (options && !strncasecmp(options, "n",1)) cn = -1;
662 while(!res && (num || playh || playa )) {
663 /* The grammar for Danish numbers is the same as for English except
664 * for the following:
665 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
666 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
667 * "one-and twenty" and 68 is "eight-and sixty".
668 * - "million" is different in singular and plural form
669 * - numbers > 1000 with zero as the third digit from last have an
670 * "and" before the last two digits, i.e. 2034 is "two thousand and
671 * four-and thirty" and 1000012 is "one million and twelve".
673 if (num < 0) {
674 snprintf(fn, sizeof(fn), "digits/minus");
675 if ( num > INT_MIN ) {
676 num = -num;
677 } else {
678 num = 0;
680 } else if (playh) {
681 snprintf(fn, sizeof(fn), "digits/hundred");
682 playh = 0;
683 } else if (playa) {
684 snprintf(fn, sizeof(fn), "digits/and");
685 playa = 0;
686 } else if (num == 1 && cn == -1) {
687 snprintf(fn, sizeof(fn), "digits/1N");
688 num = 0;
689 } else if (num < 20) {
690 snprintf(fn, sizeof(fn), "digits/%d", num);
691 num = 0;
692 } else if (num < 100) {
693 int ones = num % 10;
694 if (ones) {
695 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
696 num -= ones;
697 } else {
698 snprintf(fn, sizeof(fn), "digits/%d", num);
699 num = 0;
701 } else {
702 if (num < 1000) {
703 int hundreds = num / 100;
704 if (hundreds == 1)
705 snprintf(fn, sizeof(fn), "digits/1N");
706 else
707 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
709 playh++;
710 num -= 100 * hundreds;
711 if (num)
712 playa++;
714 } else {
715 if (num < 1000000) {
716 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
717 if (res)
718 return res;
719 num = num % 1000;
720 snprintf(fn, sizeof(fn), "digits/thousand");
721 } else {
722 if (num < 1000000000) {
723 int millions = num / 1000000;
724 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
725 if (res)
726 return res;
727 if (millions == 1)
728 snprintf(fn, sizeof(fn), "digits/million");
729 else
730 snprintf(fn, sizeof(fn), "digits/millions");
731 num = num % 1000000;
732 } else {
733 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
734 res = -1;
737 if (num && num < 100)
738 playa++;
741 if (!res) {
742 if(!ast_streamfile(chan, fn, language)) {
743 if ((audiofd > -1) && (ctrlfd > -1))
744 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
745 else
746 res = ast_waitstream(chan, ints);
748 ast_stopstream(chan);
751 return res;
754 /*! \brief ast_say_number_full_de: German syntax */
755 /* New files:
756 In addition to English, the following sounds are required:
757 "millions"
758 "1-and" through "9-and"
759 "1F" (eine)
760 "1N" (ein)
761 NB "1" is recorded as 'eins'
763 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)
765 int res = 0, t = 0;
766 int mf = 1; /* +1 = male and neuter; -1 = female */
767 char fn[256] = "";
768 char fna[256] = "";
769 if (!num)
770 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
772 if (options && (!strncasecmp(options, "f",1)))
773 mf = -1;
775 while(!res && num) {
776 /* The grammar for German numbers is the same as for English except
777 * for the following:
778 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
779 * "one-and twenty" and 68 is "eight-and sixty".
780 * - "one" varies according to gender
781 * - 100 is 'hundert', however all other instances are 'ein hundert'
782 * - 1000 is 'tausend', however all other instances are 'ein tausend'
783 * - 1000000 is always 'eine million'
784 * - "million" is different in singular and plural form
786 if (num < 0) {
787 snprintf(fn, sizeof(fn), "digits/minus");
788 if ( num > INT_MIN ) {
789 num = -num;
790 } else {
791 num = 0;
793 } else if (num < 100 && t) {
794 snprintf(fn, sizeof(fn), "digits/and");
795 t = 0;
796 } else if (num == 1 && mf == -1) {
797 snprintf(fn, sizeof(fn), "digits/%dF", num);
798 num = 0;
799 } else if (num < 20) {
800 snprintf(fn, sizeof(fn), "digits/%d", num);
801 num = 0;
802 } else if (num < 100) {
803 int ones = num % 10;
804 if (ones) {
805 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
806 num -= ones;
807 } else {
808 snprintf(fn, sizeof(fn), "digits/%d", num);
809 num = 0;
811 } else if (num == 100 && t == 0) {
812 snprintf(fn, sizeof(fn), "digits/hundred");
813 num = 0;
814 } else if (num < 1000) {
815 int hundreds = num / 100;
816 num = num % 100;
817 if (hundreds == 1) {
818 snprintf(fn, sizeof(fn), "digits/1N");
819 } else {
820 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
822 snprintf(fna, sizeof(fna), "digits/hundred");
823 t = 1;
824 } else if (num == 1000 && t == 0) {
825 snprintf(fn, sizeof(fn), "digits/thousand");
826 num = 0;
827 } else if (num < 1000000) {
828 int thousands = num / 1000;
829 num = num % 1000;
830 t = 1;
831 if (thousands == 1) {
832 snprintf(fn, sizeof(fn), "digits/1N");
833 snprintf(fna, sizeof(fna), "digits/thousand");
834 } else {
835 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
836 if (res)
837 return res;
838 snprintf(fn, sizeof(fn), "digits/thousand");
840 } else if (num < 1000000000) {
841 int millions = num / 1000000;
842 num = num % 1000000;
843 t = 1;
844 if (millions == 1) {
845 snprintf(fn, sizeof(fn), "digits/1F");
846 snprintf(fna, sizeof(fna), "digits/million");
847 } else {
848 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
849 if (res)
850 return res;
851 snprintf(fn, sizeof(fn), "digits/millions");
853 } else if (num <= INT_MAX) {
854 int billions = num / 1000000000;
855 num = num % 1000000000;
856 t = 1;
857 if (billions == 1) {
858 snprintf(fn, sizeof(fn), "digits/1F");
859 snprintf(fna, sizeof(fna), "digits/milliard");
860 } else {
861 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
862 if (res) {
863 return res;
865 snprintf(fn, sizeof(fn), "digits/milliards");
867 } else {
868 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
869 res = -1;
871 if (!res) {
872 if(!ast_streamfile(chan, fn, language)) {
873 if ((audiofd > -1) && (ctrlfd > -1))
874 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
875 else
876 res = ast_waitstream(chan, ints);
878 ast_stopstream(chan);
879 if (!res) {
880 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
881 if ((audiofd > -1) && (ctrlfd > -1))
882 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
883 else
884 res = ast_waitstream(chan, ints);
886 ast_stopstream(chan);
887 strcpy(fna, "");
891 return res;
894 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
895 /* New files:
896 In addition to American English, the following sounds are required: "and"
898 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
900 int res = 0;
901 int playh = 0;
902 int playa = 0;
903 char fn[256] = "";
904 if (!num)
905 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
907 while(!res && (num || playh || playa )) {
908 if (num < 0) {
909 snprintf(fn, sizeof(fn), "digits/minus");
910 if ( num > INT_MIN ) {
911 num = -num;
912 } else {
913 num = 0;
915 } else if (playh) {
916 snprintf(fn, sizeof(fn), "digits/hundred");
917 playh = 0;
918 } else if (playa) {
919 snprintf(fn, sizeof(fn), "digits/and");
920 playa = 0;
921 } else if (num < 20) {
922 snprintf(fn, sizeof(fn), "digits/%d", num);
923 num = 0;
924 } else if (num < 100) {
925 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
926 num -= ((num / 10) * 10);
927 } else if (num < 1000) {
928 int hundreds = num / 100;
929 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
931 playh++;
932 num -= 100 * hundreds;
933 if (num)
934 playa++;
935 } else if (num < 1000000) {
936 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
937 if (res)
938 return res;
939 snprintf(fn, sizeof(fn), "digits/thousand");
940 num = num % 1000;
941 if (num && num < 100)
942 playa++;
943 } else if (num < 1000000000) {
944 int millions = num / 1000000;
945 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
946 if (res)
947 return res;
948 snprintf(fn, sizeof(fn), "digits/million");
949 num = num % 1000000;
950 if (num && num < 100)
951 playa++;
952 } else {
953 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
954 res = -1;
957 if (!res) {
958 if(!ast_streamfile(chan, fn, language)) {
959 if ((audiofd > -1) && (ctrlfd > -1))
960 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
961 else
962 res = ast_waitstream(chan, ints);
964 ast_stopstream(chan);
967 return res;
970 /*! \brief ast_say_number_full_es: Spanish syntax */
971 /* New files:
972 Requires a few new audios:
973 1F.gsm: feminine 'una'
974 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
976 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)
978 int res = 0;
979 int playa = 0;
980 int mf = 0; /* +1 = male; -1 = female */
981 char fn[256] = "";
982 if (!num)
983 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
985 if (options) {
986 if (!strncasecmp(options, "f",1))
987 mf = -1;
988 else if (!strncasecmp(options, "m", 1))
989 mf = 1;
992 while (!res && num) {
993 if (num < 0) {
994 snprintf(fn, sizeof(fn), "digits/minus");
995 if ( num > INT_MIN ) {
996 num = -num;
997 } else {
998 num = 0;
1000 } else if (playa) {
1001 snprintf(fn, sizeof(fn), "digits/and");
1002 playa = 0;
1003 } else if (num == 1) {
1004 if (mf < 0)
1005 snprintf(fn, sizeof(fn), "digits/%dF", num);
1006 else if (mf > 0)
1007 snprintf(fn, sizeof(fn), "digits/%dM", num);
1008 else
1009 snprintf(fn, sizeof(fn), "digits/%d", num);
1010 num = 0;
1011 } else if (num < 31) {
1012 snprintf(fn, sizeof(fn), "digits/%d", num);
1013 num = 0;
1014 } else if (num < 100) {
1015 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1016 num -= ((num/10)*10);
1017 if (num)
1018 playa++;
1019 } else if (num == 100) {
1020 snprintf(fn, sizeof(fn), "digits/100");
1021 num = 0;
1022 } else if (num < 200) {
1023 snprintf(fn, sizeof(fn), "digits/100-and");
1024 num -= 100;
1025 } else {
1026 if (num < 1000) {
1027 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1028 num -= ((num/100)*100);
1029 } else if (num < 2000) {
1030 num = num % 1000;
1031 snprintf(fn, sizeof(fn), "digits/thousand");
1032 } else {
1033 if (num < 1000000) {
1034 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1035 if (res)
1036 return res;
1037 num = num % 1000;
1038 snprintf(fn, sizeof(fn), "digits/thousand");
1039 } else {
1040 if (num < 2147483640) {
1041 if ((num/1000000) == 1) {
1042 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1043 if (res)
1044 return res;
1045 snprintf(fn, sizeof(fn), "digits/million");
1046 } else {
1047 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1048 if (res)
1049 return res;
1050 snprintf(fn, sizeof(fn), "digits/millions");
1052 num = num % 1000000;
1053 } else {
1054 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1055 res = -1;
1061 if (!res) {
1062 if(!ast_streamfile(chan, fn, language)) {
1063 if ((audiofd > -1) && (ctrlfd > -1))
1064 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1065 else
1066 res = ast_waitstream(chan, ints);
1068 ast_stopstream(chan);
1073 return res;
1076 /*! \brief ast_say_number_full_fr: French syntax */
1077 /* Extra sounds needed:
1078 1F: feminin 'une'
1079 et: 'and' */
1080 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)
1082 int res = 0;
1083 int playh = 0;
1084 int playa = 0;
1085 int mf = 1; /* +1 = male; -1 = female */
1086 char fn[256] = "";
1087 if (!num)
1088 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1090 if (options && !strncasecmp(options, "f",1))
1091 mf = -1;
1093 while(!res && (num || playh || playa)) {
1094 if (num < 0) {
1095 snprintf(fn, sizeof(fn), "digits/minus");
1096 if ( num > INT_MIN ) {
1097 num = -num;
1098 } else {
1099 num = 0;
1101 } else if (playh) {
1102 snprintf(fn, sizeof(fn), "digits/hundred");
1103 playh = 0;
1104 } else if (playa) {
1105 snprintf(fn, sizeof(fn), "digits/et");
1106 playa = 0;
1107 } else if (num == 1) {
1108 if (mf < 0)
1109 snprintf(fn, sizeof(fn), "digits/%dF", num);
1110 else
1111 snprintf(fn, sizeof(fn), "digits/%d", num);
1112 num = 0;
1113 } else if (num < 21) {
1114 snprintf(fn, sizeof(fn), "digits/%d", num);
1115 num = 0;
1116 } else if (num < 70) {
1117 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1118 if ((num % 10) == 1) playa++;
1119 num = num % 10;
1120 } else if (num < 80) {
1121 snprintf(fn, sizeof(fn), "digits/60");
1122 if ((num % 10) == 1) playa++;
1123 num = num - 60;
1124 } else if (num < 100) {
1125 snprintf(fn, sizeof(fn), "digits/80");
1126 num = num - 80;
1127 } else if (num < 200) {
1128 snprintf(fn, sizeof(fn), "digits/hundred");
1129 num = num - 100;
1130 } else if (num < 1000) {
1131 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1132 playh++;
1133 num = num % 100;
1134 } else if (num < 2000) {
1135 snprintf(fn, sizeof(fn), "digits/thousand");
1136 num = num - 1000;
1137 } else if (num < 1000000) {
1138 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1139 if (res)
1140 return res;
1141 snprintf(fn, sizeof(fn), "digits/thousand");
1142 num = num % 1000;
1143 } else if (num < 1000000000) {
1144 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1145 if (res)
1146 return res;
1147 snprintf(fn, sizeof(fn), "digits/million");
1148 num = num % 1000000;
1149 } else {
1150 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1151 res = -1;
1153 if (!res) {
1154 if(!ast_streamfile(chan, fn, language)) {
1155 if ((audiofd > -1) && (ctrlfd > -1))
1156 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1157 else
1158 res = ast_waitstream(chan, ints);
1160 ast_stopstream(chan);
1163 return res;
1168 /*! \brief ast_say_number_full_he: Hebrew syntax */
1169 /* Extra sounds needed:
1170 1F: feminin 'one'
1171 ve: 'and'
1172 1hundred: 1 hundred
1173 2hundred: 2 hundreds
1174 2thousands: 2 thousand
1175 thousands: plural of 'thousand'
1176 3sF 'Smichut forms (female)
1183 3s 'Smichut' forms (male)
1200 TODO: 've' should sometimed be 'hu':
1201 * before 'shtaym' (2, F)
1202 * before 'shnaym' (2, M)
1203 * before 'shlosha' (3, M)
1204 * before 'shmone' (8, M)
1205 * before 'shlosim' (30)
1206 * before 'shmonim' (80)
1208 What about:
1209 'sheva' (7, F)?
1210 'tesha' (9, F)?
1212 #define SAY_NUM_BUF_SIZE 256
1213 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1214 const char *ints, const char *language, const char *options,
1215 int audiofd, int ctrlfd)
1217 int res = 0;
1218 int state = 0; /* no need to save anything */
1219 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1220 char fn[SAY_NUM_BUF_SIZE] = "";
1221 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1222 "num: %d, options=\"%s\"\n",
1223 num, options
1225 if (!num)
1226 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1228 if (options && !strncasecmp(options, "f",1))
1229 mf = -1;
1231 /* Do we have work to do? */
1232 while(!res && (num || (state>0) )) {
1233 /* first type of work: play a second sound. In this loop
1234 * we can only play one sound file at a time. Thus playing
1235 * a second one requires repeating the loop just for the
1236 * second file. The variable 'state' remembers where we were.
1237 * state==0 is the normal mode and it means that we continue
1238 * to check if the number num has yet anything left.
1240 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1241 "state=%d, options=\"%s\", mf=%d\n",
1242 num, state, options, mf
1244 if (state==1) {
1245 snprintf(fn, sizeof(fn), "digits/hundred");
1246 state = 0;
1247 } else if (state==2) {
1248 snprintf(fn, sizeof(fn), "digits/ve");
1249 state = 0;
1250 } else if (state==3) {
1251 snprintf(fn, sizeof(fn), "digits/thousands");
1252 state=0;
1253 } else if (num <21) {
1254 if (mf < 0)
1255 snprintf(fn, sizeof(fn), "digits/%dF", num);
1256 else
1257 snprintf(fn, sizeof(fn), "digits/%d", num);
1258 num = 0;
1259 } else if (num < 100) {
1260 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1261 num = num % 10;
1262 if (num>0) state=2;
1263 } else if (num < 200) {
1264 snprintf(fn, sizeof(fn), "digits/1hundred");
1265 num = num - 100;
1266 state=2;
1267 } else if (num < 300) {
1268 snprintf(fn, sizeof(fn), "digits/2hundred");
1269 num = num - 200;
1270 state=2;
1271 } else if (num < 1000) {
1272 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1273 state=1;
1274 num = num % 100;
1275 } else if (num < 2000) {
1276 snprintf(fn, sizeof(fn), "digits/thousand");
1277 num = num - 1000;
1278 } else if (num < 3000) {
1279 snprintf(fn, sizeof(fn), "digits/2thousand");
1280 num = num - 2000;
1281 if (num>0) state=2;
1282 } else if (num < 20000) {
1283 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1284 num = num % 1000;
1285 state=3;
1286 } else if (num < 1000000) {
1287 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1288 if (res)
1289 return res;
1290 snprintf(fn, sizeof(fn), "digits/thousand");
1291 num = num % 1000;
1292 } else if (num < 1000000000) {
1293 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1294 if (res)
1295 return res;
1296 snprintf(fn, sizeof(fn), "digits/million");
1297 num = num % 1000000;
1298 } else {
1299 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1300 res = -1;
1302 if (!res) {
1303 if(!ast_streamfile(chan, fn, language)) {
1304 if ((audiofd > -1) && (ctrlfd > -1))
1305 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1306 else
1307 res = ast_waitstream(chan, ints);
1309 ast_stopstream(chan);
1312 return res;
1315 /*! \brief ast_say_number_full_it: Italian */
1316 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1318 int res = 0;
1319 int playh = 0;
1320 int tempnum = 0;
1321 char fn[256] = "";
1323 if (!num)
1324 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1327 Italian support
1329 Like english, numbers up to 20 are a single 'word', and others
1330 compound, but with exceptions.
1331 For example 21 is not twenty-one, but there is a single word in 'it'.
1332 Idem for 28 (ie when a the 2nd part of a compund number
1333 starts with a vowel)
1335 There are exceptions also for hundred, thousand and million.
1336 In english 100 = one hundred, 200 is two hundred.
1337 In italian 100 = cento , like to say hundred (without one),
1338 200 and more are like english.
1340 Same applies for thousand:
1341 1000 is one thousand in en, 2000 is two thousand.
1342 In it we have 1000 = mille , 2000 = 2 mila
1344 For million(s) we use the plural, if more than one
1345 Also, one million is abbreviated in it, like on-million,
1346 or 'un milione', not 'uno milione'.
1347 So the right file is provided.
1350 while(!res && (num || playh)) {
1351 if (num < 0) {
1352 snprintf(fn, sizeof(fn), "digits/minus");
1353 if ( num > INT_MIN ) {
1354 num = -num;
1355 } else {
1356 num = 0;
1358 } else if (playh) {
1359 snprintf(fn, sizeof(fn), "digits/hundred");
1360 playh = 0;
1361 } else if (num < 20) {
1362 snprintf(fn, sizeof(fn), "digits/%d", num);
1363 num = 0;
1364 } else if (num == 21) {
1365 snprintf(fn, sizeof(fn), "digits/%d", num);
1366 num = 0;
1367 } else if (num == 28) {
1368 snprintf(fn, sizeof(fn), "digits/%d", num);
1369 num = 0;
1370 } else if (num == 31) {
1371 snprintf(fn, sizeof(fn), "digits/%d", num);
1372 num = 0;
1373 } else if (num == 38) {
1374 snprintf(fn, sizeof(fn), "digits/%d", num);
1375 num = 0;
1376 } else if (num == 41) {
1377 snprintf(fn, sizeof(fn), "digits/%d", num);
1378 num = 0;
1379 } else if (num == 48) {
1380 snprintf(fn, sizeof(fn), "digits/%d", num);
1381 num = 0;
1382 } else if (num == 51) {
1383 snprintf(fn, sizeof(fn), "digits/%d", num);
1384 num = 0;
1385 } else if (num == 58) {
1386 snprintf(fn, sizeof(fn), "digits/%d", num);
1387 num = 0;
1388 } else if (num == 61) {
1389 snprintf(fn, sizeof(fn), "digits/%d", num);
1390 num = 0;
1391 } else if (num == 68) {
1392 snprintf(fn, sizeof(fn), "digits/%d", num);
1393 num = 0;
1394 } else if (num == 71) {
1395 snprintf(fn, sizeof(fn), "digits/%d", num);
1396 num = 0;
1397 } else if (num == 78) {
1398 snprintf(fn, sizeof(fn), "digits/%d", num);
1399 num = 0;
1400 } else if (num == 81) {
1401 snprintf(fn, sizeof(fn), "digits/%d", num);
1402 num = 0;
1403 } else if (num == 88) {
1404 snprintf(fn, sizeof(fn), "digits/%d", num);
1405 num = 0;
1406 } else if (num == 91) {
1407 snprintf(fn, sizeof(fn), "digits/%d", num);
1408 num = 0;
1409 } else if (num == 98) {
1410 snprintf(fn, sizeof(fn), "digits/%d", num);
1411 num = 0;
1412 } else if (num < 100) {
1413 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1414 num -= ((num / 10) * 10);
1415 } else {
1416 if (num < 1000) {
1417 if ((num / 100) > 1) {
1418 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1419 playh++;
1420 } else {
1421 snprintf(fn, sizeof(fn), "digits/hundred");
1423 num -= ((num / 100) * 100);
1424 } else {
1425 if (num < 1000000) { /* 1,000,000 */
1426 if ((num/1000) > 1)
1427 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1428 if (res)
1429 return res;
1430 tempnum = num;
1431 num = num % 1000;
1432 if ((tempnum / 1000) < 2)
1433 snprintf(fn, sizeof(fn), "digits/thousand");
1434 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1435 snprintf(fn, sizeof(fn), "digits/thousands");
1436 } else {
1437 if (num < 1000000000) { /* 1,000,000,000 */
1438 if ((num / 1000000) > 1)
1439 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1440 if (res)
1441 return res;
1442 tempnum = num;
1443 num = num % 1000000;
1444 if ((tempnum / 1000000) < 2)
1445 snprintf(fn, sizeof(fn), "digits/million");
1446 else
1447 snprintf(fn, sizeof(fn), "digits/millions");
1448 } else {
1449 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1450 res = -1;
1455 if (!res) {
1456 if(!ast_streamfile(chan, fn, language)) {
1457 if ((audiofd > -1) && (ctrlfd > -1))
1458 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1459 else
1460 res = ast_waitstream(chan, ints);
1462 ast_stopstream(chan);
1465 return res;
1468 /*! \brief ast_say_number_full_nl: dutch syntax */
1469 /* New files: digits/nl-en
1471 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1473 int res = 0;
1474 int playh = 0;
1475 int units = 0;
1476 char fn[256] = "";
1477 if (!num)
1478 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1479 while (!res && (num || playh )) {
1480 if (num < 0) {
1481 snprintf(fn, sizeof(fn), "digits/minus");
1482 if ( num > INT_MIN ) {
1483 num = -num;
1484 } else {
1485 num = 0;
1487 } else if (playh) {
1488 snprintf(fn, sizeof(fn), "digits/hundred");
1489 playh = 0;
1490 } else if (num < 20) {
1491 snprintf(fn, sizeof(fn), "digits/%d", num);
1492 num = 0;
1493 } else if (num < 100) {
1494 units = num % 10;
1495 if (units > 0) {
1496 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1497 if (res)
1498 return res;
1499 num = num - units;
1500 snprintf(fn, sizeof(fn), "digits/nl-en");
1501 } else {
1502 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1503 num = 0;
1505 } else {
1506 if (num < 1000) {
1507 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1508 playh++;
1509 num -= ((num / 100) * 100);
1510 } else {
1511 if (num < 1000000) { /* 1,000,000 */
1512 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1513 if (res)
1514 return res;
1515 num = num % 1000;
1516 snprintf(fn, sizeof(fn), "digits/thousand");
1517 } else {
1518 if (num < 1000000000) { /* 1,000,000,000 */
1519 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1520 if (res)
1521 return res;
1522 num = num % 1000000;
1523 snprintf(fn, sizeof(fn), "digits/million");
1524 } else {
1525 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1526 res = -1;
1532 if (!res) {
1533 if(!ast_streamfile(chan, fn, language)) {
1534 if ((audiofd > -1) && (ctrlfd > -1))
1535 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1536 else
1537 res = ast_waitstream(chan, ints);
1539 ast_stopstream(chan);
1542 return res;
1545 /*! \brief ast_say_number_full_no: Norwegian syntax */
1546 /* New files:
1547 In addition to American English, the following sounds are required: "and", "1N"
1549 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)
1551 int res = 0;
1552 int playh = 0;
1553 int playa = 0;
1554 int cn = 1; /* +1 = commune; -1 = neuter */
1555 char fn[256] = "";
1557 if (!num)
1558 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1560 if (options && !strncasecmp(options, "n",1)) cn = -1;
1562 while(!res && (num || playh || playa )) {
1563 /* The grammar for Norwegian numbers is the same as for English except
1564 * for the following:
1565 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1566 * "and" before the last two digits, i.e. 2034 is "two thousand and
1567 * thirty-four" and 1000012 is "one million and twelve".
1569 if (num < 0) {
1570 snprintf(fn, sizeof(fn), "digits/minus");
1571 if ( num > INT_MIN ) {
1572 num = -num;
1573 } else {
1574 num = 0;
1576 } else if (playh) {
1577 snprintf(fn, sizeof(fn), "digits/hundred");
1578 playh = 0;
1579 } else if (playa) {
1580 snprintf(fn, sizeof(fn), "digits/and");
1581 playa = 0;
1582 } else if (num == 1 && cn == -1) {
1583 snprintf(fn, sizeof(fn), "digits/1N");
1584 num = 0;
1585 } else if (num < 20) {
1586 snprintf(fn, sizeof(fn), "digits/%d", num);
1587 num = 0;
1588 } else if (num < 100) {
1589 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1590 num -= ((num / 10) * 10);
1591 } else if (num < 1000) {
1592 int hundreds = num / 100;
1593 if (hundreds == 1)
1594 snprintf(fn, sizeof(fn), "digits/1N");
1595 else
1596 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1598 playh++;
1599 num -= 100 * hundreds;
1600 if (num)
1601 playa++;
1602 } else if (num < 1000000) {
1603 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1604 if (res)
1605 return res;
1606 snprintf(fn, sizeof(fn), "digits/thousand");
1607 num = num % 1000;
1608 if (num && num < 100)
1609 playa++;
1610 } else if (num < 1000000000) {
1611 int millions = num / 1000000;
1612 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1613 if (res)
1614 return res;
1615 snprintf(fn, sizeof(fn), "digits/million");
1616 num = num % 1000000;
1617 if (num && num < 100)
1618 playa++;
1619 } else {
1620 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1621 res = -1;
1624 if (!res) {
1625 if(!ast_streamfile(chan, fn, language)) {
1626 if ((audiofd > -1) && (ctrlfd > -1))
1627 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1628 else
1629 res = ast_waitstream(chan, ints);
1631 ast_stopstream(chan);
1634 return res;
1637 typedef struct {
1638 char *separator_dziesiatek;
1639 char *cyfry[10];
1640 char *cyfry2[10];
1641 char *setki[10];
1642 char *dziesiatki[10];
1643 char *nastki[10];
1644 char *rzedy[3][3];
1645 } odmiana;
1647 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1649 if (rzad==0)
1650 return "";
1652 if (i==1)
1653 return odm->rzedy[rzad - 1][0];
1654 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1655 return odm->rzedy[rzad - 1][1];
1656 else
1657 return odm->rzedy[rzad - 1][2];
1660 static char* pl_append(char* buffer, char* str)
1662 strcpy(buffer, str);
1663 buffer += strlen(str);
1664 return buffer;
1667 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1669 char file_name[255] = "digits/";
1670 strcat(file_name, fn);
1671 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1672 if (!ast_streamfile(chan, file_name, language)) {
1673 if ((audiofd > -1) && (ctrlfd > -1))
1674 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1675 else
1676 ast_waitstream(chan, ints);
1678 ast_stopstream(chan);
1681 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1683 /* Initialise variables to allow compilation on Debian-stable, etc */
1684 int m1000E6 = 0;
1685 int i1000E6 = 0;
1686 int m1000E3 = 0;
1687 int i1000E3 = 0;
1688 int m1000 = 0;
1689 int i1000 = 0;
1690 int m100 = 0;
1691 int i100 = 0;
1693 if (i == 0 && rzad > 0) {
1694 return;
1696 if (i == 0) {
1697 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1698 return;
1701 m1000E6 = i % 1000000000;
1702 i1000E6 = i / 1000000000;
1704 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1706 m1000E3 = m1000E6 % 1000000;
1707 i1000E3 = m1000E6 / 1000000;
1709 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1711 m1000 = m1000E3 % 1000;
1712 i1000 = m1000E3 / 1000;
1714 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1716 m100 = m1000 % 100;
1717 i100 = m1000 / 100;
1719 if (i100>0)
1720 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1722 if ( m100 > 0 && m100 <=9 ) {
1723 if (m1000>0)
1724 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1725 else
1726 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1727 } else if (m100 % 10 == 0) {
1728 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1729 } else if (m100 <= 19 ) {
1730 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1731 } else if (m100 != 0) {
1732 if (odm->separator_dziesiatek[0]==' ') {
1733 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1734 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1735 } else {
1736 char buf[10];
1737 char *b = buf;
1738 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1739 b = pl_append(b, odm->separator_dziesiatek);
1740 b = pl_append(b, odm->cyfry2[m100 % 10]);
1741 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1745 if (rzad > 0) {
1746 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1750 /* ast_say_number_full_pl: Polish syntax */
1751 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)
1753 Sounds needed:
1754 0 zero
1755 1 jeden
1756 10 dziesiec
1757 100 sto
1758 1000 tysiac
1759 1000000 milion
1760 1000000000 miliard
1761 1000000000.2 miliardy
1762 1000000000.5 miliardow
1763 1000000.2 miliony
1764 1000000.5 milionow
1765 1000.2 tysiace
1766 1000.5 tysiecy
1767 100m stu
1768 10m dziesieciu
1769 11 jedenascie
1770 11m jedenastu
1771 12 dwanascie
1772 12m dwunastu
1773 13 trzynascie
1774 13m trzynastu
1775 14 czternascie
1776 14m czternastu
1777 15 pietnascie
1778 15m pietnastu
1779 16 szesnascie
1780 16m szesnastu
1781 17 siedemnascie
1782 17m siedemnastu
1783 18 osiemnascie
1784 18m osiemnastu
1785 19 dziewietnascie
1786 19m dziewietnastu
1787 1z jedna
1788 2 dwa
1789 20 dwadziescia
1790 200 dwiescie
1791 200m dwustu
1792 20m dwudziestu
1793 2-1m dwaj
1794 2-2m dwoch
1795 2z dwie
1796 3 trzy
1797 30 trzydziesci
1798 300 trzysta
1799 300m trzystu
1800 30m trzydziestu
1801 3-1m trzej
1802 3-2m trzech
1803 4 cztery
1804 40 czterdziesci
1805 400 czterysta
1806 400m czterystu
1807 40m czterdziestu
1808 4-1m czterej
1809 4-2m czterech
1810 5 piec
1811 50 piecdziesiat
1812 500 piecset
1813 500m pieciuset
1814 50m piedziesieciu
1815 5m pieciu
1816 6 szesc
1817 60 szescdziesiat
1818 600 szescset
1819 600m szesciuset
1820 60m szescdziesieciu
1821 6m szesciu
1822 7 siedem
1823 70 siedemdziesiat
1824 700 siedemset
1825 700m siedmiuset
1826 70m siedemdziesieciu
1827 7m siedmiu
1828 8 osiem
1829 80 osiemdziesiat
1830 800 osiemset
1831 800m osmiuset
1832 80m osiemdziesieciu
1833 8m osmiu
1834 9 dziewiec
1835 90 dziewiecdziesiat
1836 900 dziewiecset
1837 900m dziewieciuset
1838 90m dziewiedziesieciu
1839 9m dziewieciu
1840 and combinations of eg.: 20_1, 30m_3m, etc...
1844 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1846 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1848 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1850 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1852 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1854 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1856 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1858 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1860 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1862 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1864 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1866 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1868 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1870 /* Initialise variables to allow compilation on Debian-stable, etc */
1871 odmiana *o;
1873 static odmiana *odmiana_nieosobowa = NULL;
1874 static odmiana *odmiana_meska = NULL;
1875 static odmiana *odmiana_zenska = NULL;
1877 if (odmiana_nieosobowa == NULL) {
1878 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1880 odmiana_nieosobowa->separator_dziesiatek = " ";
1882 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1883 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1884 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1885 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1886 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1887 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1890 if (odmiana_zenska == NULL) {
1891 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1893 odmiana_zenska->separator_dziesiatek = " ";
1895 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1896 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1897 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1898 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1899 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1900 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1903 if (odmiana_meska == NULL) {
1904 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1906 odmiana_meska->separator_dziesiatek = " ";
1908 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1909 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1910 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1911 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1912 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1913 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1916 if (options) {
1917 if (strncasecmp(options, "f", 1) == 0)
1918 o = odmiana_zenska;
1919 else if (strncasecmp(options, "m", 1) == 0)
1920 o = odmiana_meska;
1921 else
1922 o = odmiana_nieosobowa;
1923 } else
1924 o = odmiana_nieosobowa;
1926 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1927 return 0;
1930 /* ast_say_number_full_pt: Portuguese syntax */
1931 /* Extra sounds needed: */
1932 /* For feminin all sound files end with F */
1933 /* 100E for 100+ something */
1934 /* 1000000S for plural */
1935 /* pt-e for 'and' */
1936 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)
1938 int res = 0;
1939 int playh = 0;
1940 int mf = 1; /* +1 = male; -1 = female */
1941 char fn[256] = "";
1943 if (!num)
1944 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1946 if (options && !strncasecmp(options, "f",1))
1947 mf = -1;
1949 while(!res && num ) {
1950 if (num < 0) {
1951 snprintf(fn, sizeof(fn), "digits/minus");
1952 if ( num > INT_MIN ) {
1953 num = -num;
1954 } else {
1955 num = 0;
1957 } else if (num < 20) {
1958 if ((num == 1 || num == 2) && (mf < 0))
1959 snprintf(fn, sizeof(fn), "digits/%dF", num);
1960 else
1961 snprintf(fn, sizeof(fn), "digits/%d", num);
1962 num = 0;
1963 } else if (num < 100) {
1964 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1965 if (num % 10)
1966 playh = 1;
1967 num = num % 10;
1968 } else if (num < 1000) {
1969 if (num == 100)
1970 snprintf(fn, sizeof(fn), "digits/100");
1971 else if (num < 200)
1972 snprintf(fn, sizeof(fn), "digits/100E");
1973 else {
1974 if (mf < 0 && num > 199)
1975 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1976 else
1977 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1978 if (num % 100)
1979 playh = 1;
1981 num = num % 100;
1982 } else if (num < 1000000) {
1983 if (num > 1999) {
1984 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1985 if (res)
1986 return res;
1988 snprintf(fn, sizeof(fn), "digits/1000");
1989 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1990 playh = 1;
1991 num = num % 1000;
1992 } else if (num < 1000000000) {
1993 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1994 if (res)
1995 return res;
1996 if (num < 2000000)
1997 snprintf(fn, sizeof(fn), "digits/1000000");
1998 else
1999 snprintf(fn, sizeof(fn), "digits/1000000S");
2001 if ((num % 1000000) &&
2002 /* no thousands */
2003 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2004 /* no hundreds and below */
2005 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2006 playh = 1;
2007 num = num % 1000000;
2008 } else {
2009 /* number is too big */
2010 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2011 res = -1;
2013 if (!res) {
2014 if (!ast_streamfile(chan, fn, language)) {
2015 if ((audiofd > -1) && (ctrlfd > -1))
2016 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2017 else
2018 res = ast_waitstream(chan, ints);
2020 ast_stopstream(chan);
2022 if (!res && playh) {
2023 res = wait_file(chan, ints, "digits/pt-e", language);
2024 ast_stopstream(chan);
2025 playh = 0;
2028 return res;
2031 /*! \brief ast_say_number_full_se: Swedish syntax */
2032 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)
2034 int res = 0;
2035 int playh = 0;
2036 char fn[256] = "";
2037 int cn = 1; /* +1 = commune; -1 = neuter */
2038 if (!num)
2039 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2040 if (options && !strncasecmp(options, "n",1)) cn = -1;
2042 while(!res && (num || playh)) {
2043 if (num < 0) {
2044 snprintf(fn, sizeof(fn), "digits/minus");
2045 if ( num > INT_MIN ) {
2046 num = -num;
2047 } else {
2048 num = 0;
2050 } else if (playh) {
2051 snprintf(fn, sizeof(fn), "digits/hundred");
2052 playh = 0;
2053 } else if (num < 20) {
2054 snprintf(fn, sizeof(fn), "digits/%d", num);
2055 num = 0;
2056 } else if (num < 100) {
2057 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2058 num -= ((num / 10) * 10);
2059 } else if (num == 1 && cn == -1) { /* En eller ett? */
2060 snprintf(fn, sizeof(fn), "digits/1N");
2061 num = 0;
2062 } else {
2063 if (num < 1000){
2064 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2065 playh++;
2066 num -= ((num / 100) * 100);
2067 } else {
2068 if (num < 1000000) { /* 1,000,000 */
2069 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2070 if (res) {
2071 return res;
2073 num = num % 1000;
2074 snprintf(fn, sizeof(fn), "digits/thousand");
2075 } else {
2076 if (num < 1000000000) { /* 1,000,000,000 */
2077 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2078 if (res) {
2079 return res;
2081 num = num % 1000000;
2082 snprintf(fn, sizeof(fn), "digits/million");
2083 } else {
2084 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2085 res = -1;
2090 if (!res) {
2091 if(!ast_streamfile(chan, fn, language)) {
2092 if ((audiofd > -1) && (ctrlfd > -1))
2093 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2094 else
2095 res = ast_waitstream(chan, ints);
2096 ast_stopstream(chan);
2100 return res;
2103 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2104 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2106 int res = 0;
2107 int playh = 0;
2108 char fn[256] = "";
2109 if (!num)
2110 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2112 while(!res && (num || playh)) {
2113 if (num < 0) {
2114 snprintf(fn, sizeof(fn), "digits/minus");
2115 if ( num > INT_MIN ) {
2116 num = -num;
2117 } else {
2118 num = 0;
2120 } else if (playh) {
2121 snprintf(fn, sizeof(fn), "digits/hundred");
2122 playh = 0;
2123 } else if (num < 10) {
2124 snprintf(fn, sizeof(fn), "digits/%d", num);
2125 num = 0;
2126 } else if (num < 100) {
2127 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2128 num -= ((num / 10) * 10);
2129 } else {
2130 if (num < 1000){
2131 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2132 playh++;
2133 num -= ((num / 100) * 100);
2134 } else {
2135 if (num < 1000000) { /* 1,000,000 */
2136 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2137 if (res)
2138 return res;
2139 num = num % 1000;
2140 snprintf(fn, sizeof(fn), "digits/thousand");
2141 } else {
2142 if (num < 1000000000) { /* 1,000,000,000 */
2143 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2144 if (res)
2145 return res;
2146 num = num % 1000000;
2147 snprintf(fn, sizeof(fn), "digits/million");
2148 } else {
2149 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2150 res = -1;
2155 if (!res) {
2156 if(!ast_streamfile(chan, fn, language)) {
2157 if ((audiofd > -1) && (ctrlfd > -1))
2158 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2159 else
2160 res = ast_waitstream(chan, ints);
2162 ast_stopstream(chan);
2165 return res;
2169 /*! \brief determine last digits for thousands/millions (ru) */
2170 static int get_lastdigits_ru(int num) {
2171 if (num < 20) {
2172 return num;
2173 } else if (num < 100) {
2174 return get_lastdigits_ru(num % 10);
2175 } else if (num < 1000) {
2176 return get_lastdigits_ru(num % 100);
2178 return 0; /* number too big */
2182 /*! \brief ast_say_number_full_ru: Russian syntax */
2183 /*! \brief additional files:
2184 n00.gsm (one hundred, two hundred, ...)
2185 thousand.gsm
2186 million.gsm
2187 thousands-i.gsm (tisyachi)
2188 million-a.gsm (milliona)
2189 thousands.gsm
2190 millions.gsm
2191 1f.gsm (odna)
2192 2f.gsm (dve)
2194 where 'n' from 1 to 9
2196 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)
2198 int res = 0;
2199 int lastdigits = 0;
2200 char fn[256] = "";
2201 if (!num)
2202 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2204 while(!res && (num)) {
2205 if (num < 0) {
2206 snprintf(fn, sizeof(fn), "digits/minus");
2207 if ( num > INT_MIN ) {
2208 num = -num;
2209 } else {
2210 num = 0;
2212 } else if (num < 20) {
2213 if(options && strlen(options) == 1 && num < 3) {
2214 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2215 } else {
2216 snprintf(fn, sizeof(fn), "digits/%d", num);
2218 num = 0;
2219 } else if (num < 100) {
2220 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2221 num %= 10;
2222 } else if (num < 1000){
2223 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2224 num %= 100;
2225 } else if (num < 1000000) { /* 1,000,000 */
2226 lastdigits = get_lastdigits_ru(num / 1000);
2227 /* say thousands */
2228 if (lastdigits < 3) {
2229 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2230 } else {
2231 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2233 if (res)
2234 return res;
2235 if (lastdigits == 1) {
2236 snprintf(fn, sizeof(fn), "digits/thousand");
2237 } else if (lastdigits > 1 && lastdigits < 5) {
2238 snprintf(fn, sizeof(fn), "digits/thousands-i");
2239 } else {
2240 snprintf(fn, sizeof(fn), "digits/thousands");
2242 num %= 1000;
2243 } else if (num < 1000000000) { /* 1,000,000,000 */
2244 lastdigits = get_lastdigits_ru(num / 1000000);
2245 /* say millions */
2246 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2247 if (res)
2248 return res;
2249 if (lastdigits == 1) {
2250 snprintf(fn, sizeof(fn), "digits/million");
2251 } else if (lastdigits > 1 && lastdigits < 5) {
2252 snprintf(fn, sizeof(fn), "digits/million-a");
2253 } else {
2254 snprintf(fn, sizeof(fn), "digits/millions");
2256 num %= 1000000;
2257 } else {
2258 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2259 res = -1;
2261 if (!res) {
2262 if (!ast_streamfile(chan, fn, language)) {
2263 if ((audiofd > -1) && (ctrlfd > -1))
2264 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2265 else
2266 res = ast_waitstream(chan, ints);
2268 ast_stopstream(chan);
2271 return res;
2275 /*! \brief ast_say_enumeration_full: call language-specific functions */
2276 /* Called from AGI */
2277 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2279 if (!strcasecmp(language,"en") ) { /* English syntax */
2280 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2281 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2282 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2283 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2284 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2287 /* Default to english */
2288 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2291 /*! \brief ast_say_enumeration_full_en: English syntax */
2292 /* This is the default syntax, if no other syntax defined in this file is used */
2293 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2295 int res = 0, t = 0;
2296 char fn[256] = "";
2298 while(!res && num) {
2299 if (num < 0) {
2300 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2301 if ( num > INT_MIN ) {
2302 num = -num;
2303 } else {
2304 num = 0;
2306 } else if (num < 20) {
2307 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2308 num = 0;
2309 } else if (num < 100) {
2310 int tens = num / 10;
2311 num = num % 10;
2312 if (num == 0) {
2313 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2314 } else {
2315 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2317 } else if (num < 1000) {
2318 int hundreds = num / 100;
2319 num = num % 100;
2320 if (hundreds > 1 || t == 1) {
2321 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2323 if (res)
2324 return res;
2325 if (num) {
2326 snprintf(fn, sizeof(fn), "digits/hundred");
2327 } else {
2328 snprintf(fn, sizeof(fn), "digits/h-hundred");
2330 } else if (num < 1000000) {
2331 int thousands = num / 1000;
2332 num = num % 1000;
2333 if (thousands > 1 || t == 1) {
2334 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2336 if (res)
2337 return res;
2338 if (num) {
2339 snprintf(fn, sizeof(fn), "digits/thousand");
2340 } else {
2341 snprintf(fn, sizeof(fn), "digits/h-thousand");
2343 t = 1;
2344 } else if (num < 1000000000) {
2345 int millions = num / 1000000;
2346 num = num % 1000000;
2347 t = 1;
2348 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2349 if (res)
2350 return res;
2351 if (num) {
2352 snprintf(fn, sizeof(fn), "digits/million");
2353 } else {
2354 snprintf(fn, sizeof(fn), "digits/h-million");
2356 } else if (num < INT_MAX) {
2357 int billions = num / 1000000000;
2358 num = num % 1000000000;
2359 t = 1;
2360 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2361 if (res)
2362 return res;
2363 if (num) {
2364 snprintf(fn, sizeof(fn), "digits/billion");
2365 } else {
2366 snprintf(fn, sizeof(fn), "digits/h-billion");
2368 } else if (num == INT_MAX) {
2369 snprintf(fn, sizeof(fn), "digits/h-last");
2370 num = 0;
2371 } else {
2372 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2373 res = -1;
2376 if (!res) {
2377 if (!ast_streamfile(chan, fn, language)) {
2378 if ((audiofd > -1) && (ctrlfd > -1)) {
2379 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2380 } else {
2381 res = ast_waitstream(chan, ints);
2384 ast_stopstream(chan);
2387 return res;
2390 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2391 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)
2393 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2394 int res = 0, t = 0;
2395 char fn[256] = "", fna[256] = "";
2396 char *gender;
2398 if (options && !strncasecmp(options, "f",1)) {
2399 gender = "F";
2400 } else if (options && !strncasecmp(options, "n",1)) {
2401 gender = "N";
2402 } else {
2403 gender = "";
2406 if (!num)
2407 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2409 while(!res && num) {
2410 if (num < 0) {
2411 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2412 if ( num > INT_MIN ) {
2413 num = -num;
2414 } else {
2415 num = 0;
2417 } else if (num < 100 && t) {
2418 snprintf(fn, sizeof(fn), "digits/and");
2419 t = 0;
2420 } else if (num < 20) {
2421 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2422 num = 0;
2423 } else if (num < 100) {
2424 int ones = num % 10;
2425 if (ones) {
2426 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2427 num -= ones;
2428 } else {
2429 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2430 num = 0;
2432 } else if (num == 100 && t == 0) {
2433 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2434 num = 0;
2435 } else if (num < 1000) {
2436 int hundreds = num / 100;
2437 num = num % 100;
2438 if (hundreds == 1) {
2439 snprintf(fn, sizeof(fn), "digits/1N");
2440 } else {
2441 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2443 if (num) {
2444 snprintf(fna, sizeof(fna), "digits/hundred");
2445 } else {
2446 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2448 t = 1;
2449 } else if (num < 1000000) {
2450 int thousands = num / 1000;
2451 num = num % 1000;
2452 if (thousands == 1) {
2453 if (num) {
2454 snprintf(fn, sizeof(fn), "digits/1N");
2455 snprintf(fna, sizeof(fna), "digits/thousand");
2456 } else {
2457 if (t) {
2458 snprintf(fn, sizeof(fn), "digits/1N");
2459 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2460 } else {
2461 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2464 } else {
2465 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2466 if (res) {
2467 return res;
2469 if (num) {
2470 snprintf(fn, sizeof(fn), "digits/thousand");
2471 } else {
2472 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2475 t = 1;
2476 } else if (num < 1000000000) {
2477 int millions = num / 1000000;
2478 num = num % 1000000;
2479 if (millions == 1) {
2480 if (num) {
2481 snprintf(fn, sizeof(fn), "digits/1F");
2482 snprintf(fna, sizeof(fna), "digits/million");
2483 } else {
2484 snprintf(fn, sizeof(fn), "digits/1N");
2485 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2487 } else {
2488 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2489 if (res) {
2490 return res;
2492 if (num) {
2493 snprintf(fn, sizeof(fn), "digits/millions");
2494 } else {
2495 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2498 t = 1;
2499 } else if (num < INT_MAX) {
2500 int billions = num / 1000000000;
2501 num = num % 1000000000;
2502 if (billions == 1) {
2503 if (num) {
2504 snprintf(fn, sizeof(fn), "digits/1F");
2505 snprintf(fna, sizeof(fna), "digits/milliard");
2506 } else {
2507 snprintf(fn, sizeof(fn), "digits/1N");
2508 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2510 } else {
2511 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2512 if (res)
2513 return res;
2514 if (num) {
2515 snprintf(fn, sizeof(fna), "digits/milliards");
2516 } else {
2517 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2520 t = 1;
2521 } else if (num == INT_MAX) {
2522 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2523 num = 0;
2524 } else {
2525 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2526 res = -1;
2529 if (!res) {
2530 if (!ast_streamfile(chan, fn, language)) {
2531 if ((audiofd > -1) && (ctrlfd > -1))
2532 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2533 else
2534 res = ast_waitstream(chan, ints);
2536 ast_stopstream(chan);
2537 if (!res) {
2538 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2539 if ((audiofd > -1) && (ctrlfd > -1)) {
2540 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2541 } else {
2542 res = ast_waitstream(chan, ints);
2545 ast_stopstream(chan);
2546 strcpy(fna, "");
2550 return res;
2553 /*! \brief ast_say_enumeration_full_de: German syntax */
2554 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)
2556 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2557 int res = 0, t = 0;
2558 char fn[256] = "", fna[256] = "";
2559 char *gender;
2561 if (options && !strncasecmp(options, "f",1)) {
2562 gender = "F";
2563 } else if (options && !strncasecmp(options, "n",1)) {
2564 gender = "N";
2565 } else {
2566 gender = "";
2569 if (!num)
2570 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2572 while(!res && num) {
2573 if (num < 0) {
2574 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2575 if ( num > INT_MIN ) {
2576 num = -num;
2577 } else {
2578 num = 0;
2580 } else if (num < 100 && t) {
2581 snprintf(fn, sizeof(fn), "digits/and");
2582 t = 0;
2583 } else if (num < 20) {
2584 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2585 num = 0;
2586 } else if (num < 100) {
2587 int ones = num % 10;
2588 if (ones) {
2589 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2590 num -= ones;
2591 } else {
2592 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2593 num = 0;
2595 } else if (num == 100 && t == 0) {
2596 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2597 num = 0;
2598 } else if (num < 1000) {
2599 int hundreds = num / 100;
2600 num = num % 100;
2601 if (hundreds == 1) {
2602 snprintf(fn, sizeof(fn), "digits/1N");
2603 } else {
2604 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2606 if (num) {
2607 snprintf(fna, sizeof(fna), "digits/hundred");
2608 } else {
2609 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2611 t = 1;
2612 } else if (num < 1000000) {
2613 int thousands = num / 1000;
2614 num = num % 1000;
2615 if (thousands == 1) {
2616 if (num) {
2617 snprintf(fn, sizeof(fn), "digits/1N");
2618 snprintf(fna, sizeof(fna), "digits/thousand");
2619 } else {
2620 if (t) {
2621 snprintf(fn, sizeof(fn), "digits/1N");
2622 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2623 } else {
2624 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2627 } else {
2628 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2629 if (res) {
2630 return res;
2632 if (num) {
2633 snprintf(fn, sizeof(fn), "digits/thousand");
2634 } else {
2635 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2638 t = 1;
2639 } else if (num < 1000000000) {
2640 int millions = num / 1000000;
2641 num = num % 1000000;
2642 if (millions == 1) {
2643 if (num) {
2644 snprintf(fn, sizeof(fn), "digits/1F");
2645 snprintf(fna, sizeof(fna), "digits/million");
2646 } else {
2647 snprintf(fn, sizeof(fn), "digits/1N");
2648 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2650 } else {
2651 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2652 if (res) {
2653 return res;
2655 if (num) {
2656 snprintf(fn, sizeof(fn), "digits/millions");
2657 } else {
2658 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2661 t = 1;
2662 } else if (num < INT_MAX) {
2663 int billions = num / 1000000000;
2664 num = num % 1000000000;
2665 if (billions == 1) {
2666 if (num) {
2667 snprintf(fn, sizeof(fn), "digits/1F");
2668 snprintf(fna, sizeof(fna), "digits/milliard");
2669 } else {
2670 snprintf(fn, sizeof(fn), "digits/1N");
2671 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2673 } else {
2674 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2675 if (res)
2676 return res;
2677 if (num) {
2678 snprintf(fn, sizeof(fna), "digits/milliards");
2679 } else {
2680 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2683 t = 1;
2684 } else if (num == INT_MAX) {
2685 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2686 num = 0;
2687 } else {
2688 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2689 res = -1;
2692 if (!res) {
2693 if (!ast_streamfile(chan, fn, language)) {
2694 if ((audiofd > -1) && (ctrlfd > -1))
2695 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2696 else
2697 res = ast_waitstream(chan, ints);
2699 ast_stopstream(chan);
2700 if (!res) {
2701 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2702 if ((audiofd > -1) && (ctrlfd > -1)) {
2703 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2704 } else {
2705 res = ast_waitstream(chan, ints);
2708 ast_stopstream(chan);
2709 strcpy(fna, "");
2713 return res;
2716 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2718 if (!strcasecmp(lang, "en") ) { /* English syntax */
2719 return(ast_say_date_en(chan, t, ints, lang));
2720 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2721 return(ast_say_date_da(chan, t, ints, lang));
2722 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2723 return(ast_say_date_de(chan, t, ints, lang));
2724 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2725 return(ast_say_date_fr(chan, t, ints, lang));
2726 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2727 return(ast_say_date_nl(chan, t, ints, lang));
2728 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2729 return(ast_say_date_pt(chan, t, ints, lang));
2730 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2731 return(ast_say_date_gr(chan, t, ints, lang));
2732 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
2733 return(ast_say_date_ge(chan, t, ints, lang));
2736 /* Default to English */
2737 return(ast_say_date_en(chan, t, ints, lang));
2740 /* English syntax */
2741 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2743 struct tm tm;
2744 char fn[256];
2745 int res = 0;
2746 ast_localtime(&t,&tm,NULL);
2747 if (!res) {
2748 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2749 res = ast_streamfile(chan, fn, lang);
2750 if (!res)
2751 res = ast_waitstream(chan, ints);
2753 if (!res) {
2754 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2755 res = ast_streamfile(chan, fn, lang);
2756 if (!res)
2757 res = ast_waitstream(chan, ints);
2759 if (!res)
2760 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2761 if (!res)
2762 res = ast_waitstream(chan, ints);
2763 if (!res)
2764 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2765 return res;
2768 /* Danish syntax */
2769 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2771 struct tm tm;
2772 char fn[256];
2773 int res = 0;
2774 ast_localtime(&t,&tm,NULL);
2775 if (!res) {
2776 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2777 res = ast_streamfile(chan, fn, lang);
2778 if (!res)
2779 res = ast_waitstream(chan, ints);
2781 if (!res)
2782 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2783 if (!res)
2784 res = ast_waitstream(chan, ints);
2785 if (!res) {
2786 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2787 res = ast_streamfile(chan, fn, lang);
2788 if (!res)
2789 res = ast_waitstream(chan, ints);
2791 if (!res) {
2792 /* Year */
2793 int year = tm.tm_year + 1900;
2794 if (year > 1999) { /* year 2000 and later */
2795 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2796 } else {
2797 if (year < 1100) {
2798 /* I'm not going to handle 1100 and prior */
2799 /* We'll just be silent on the year, instead of bombing out. */
2800 } else {
2801 /* year 1100 to 1999. will anybody need this?!? */
2802 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2803 res = wait_file(chan, ints, fn, lang);
2804 if (!res) {
2805 res = wait_file(chan,ints, "digits/hundred", lang);
2806 if (!res && year % 100 != 0) {
2807 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2813 return res;
2816 /* German syntax */
2817 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2819 struct tm tm;
2820 char fn[256];
2821 int res = 0;
2822 ast_localtime(&t,&tm,NULL);
2823 if (!res) {
2824 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2825 res = ast_streamfile(chan, fn, lang);
2826 if (!res)
2827 res = ast_waitstream(chan, ints);
2829 if (!res)
2830 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2831 if (!res)
2832 res = ast_waitstream(chan, ints);
2833 if (!res) {
2834 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2835 res = ast_streamfile(chan, fn, lang);
2836 if (!res)
2837 res = ast_waitstream(chan, ints);
2839 if (!res) {
2840 /* Year */
2841 int year = tm.tm_year + 1900;
2842 if (year > 1999) { /* year 2000 and later */
2843 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2844 } else {
2845 if (year < 1100) {
2846 /* I'm not going to handle 1100 and prior */
2847 /* We'll just be silent on the year, instead of bombing out. */
2848 } else {
2849 /* year 1100 to 1999. will anybody need this?!? */
2850 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2851 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2852 res = wait_file(chan, ints, fn, lang);
2853 if (!res) {
2854 res = wait_file(chan,ints, "digits/hundred", lang);
2855 if (!res && year % 100 != 0) {
2856 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2862 return res;
2865 /* French syntax */
2866 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2868 struct tm tm;
2869 char fn[256];
2870 int res = 0;
2871 ast_localtime(&t,&tm,NULL);
2872 if (!res) {
2873 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2874 res = ast_streamfile(chan, fn, lang);
2875 if (!res)
2876 res = ast_waitstream(chan, ints);
2878 if (!res)
2879 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2880 if (!res)
2881 res = ast_waitstream(chan, ints);
2882 if (!res) {
2883 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2884 res = ast_streamfile(chan, fn, lang);
2885 if (!res)
2886 res = ast_waitstream(chan, ints);
2888 if (!res)
2889 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2890 return res;
2893 /* Dutch syntax */
2894 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2896 struct tm tm;
2897 char fn[256];
2898 int res = 0;
2899 ast_localtime(&t,&tm,NULL);
2900 if (!res) {
2901 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2902 res = ast_streamfile(chan, fn, lang);
2903 if (!res)
2904 res = ast_waitstream(chan, ints);
2906 if (!res)
2907 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2908 if (!res) {
2909 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2910 res = ast_streamfile(chan, fn, lang);
2911 if (!res)
2912 res = ast_waitstream(chan, ints);
2914 if (!res)
2915 res = ast_waitstream(chan, ints);
2916 if (!res)
2917 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2918 return res;
2921 /* Portuguese syntax */
2922 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2924 struct tm tm;
2925 char fn[256];
2926 int res = 0;
2928 ast_localtime(&t, &tm, NULL);
2929 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2930 if (!res)
2931 res = wait_file(chan, ints, fn, lang);
2932 if (!res)
2933 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2934 if (!res)
2935 res = wait_file(chan, ints, "digits/pt-de", lang);
2936 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2937 if (!res)
2938 res = wait_file(chan, ints, fn, lang);
2939 if (!res)
2940 res = wait_file(chan, ints, "digits/pt-de", lang);
2941 if (!res)
2942 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2944 return res;
2947 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)
2949 if (!strcasecmp(lang, "en") ) { /* English syntax */
2950 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2951 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2952 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
2953 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2954 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
2955 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
2956 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
2957 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2958 return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
2959 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2960 return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
2961 } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
2962 return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
2963 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2964 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
2965 } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
2966 return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
2967 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2968 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
2969 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
2970 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
2971 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2972 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
2975 /* Default to English */
2976 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2979 /* English syntax */
2980 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)
2982 struct tm tm;
2983 int res=0, offset, sndoffset;
2984 char sndfile[256], nextmsg[256];
2986 if (format == NULL)
2987 format = "ABdY 'digits/at' IMp";
2989 ast_localtime(&time,&tm,timezone);
2991 for (offset=0 ; format[offset] != '\0' ; offset++) {
2992 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2993 switch (format[offset]) {
2994 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2995 case '\'':
2996 /* Literal name of a sound file */
2997 sndoffset=0;
2998 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2999 sndfile[sndoffset] = format[offset];
3000 sndfile[sndoffset] = '\0';
3001 res = wait_file(chan,ints,sndfile,lang);
3002 break;
3003 case 'A':
3004 case 'a':
3005 /* Sunday - Saturday */
3006 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3007 res = wait_file(chan,ints,nextmsg,lang);
3008 break;
3009 case 'B':
3010 case 'b':
3011 case 'h':
3012 /* January - December */
3013 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3014 res = wait_file(chan,ints,nextmsg,lang);
3015 break;
3016 case 'm':
3017 /* Month enumerated */
3018 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
3019 break;
3020 case 'd':
3021 case 'e':
3022 /* First - Thirtyfirst */
3023 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
3024 break;
3025 case 'Y':
3026 /* Year */
3027 if (tm.tm_year > 99) {
3028 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3029 } else if (tm.tm_year < 1) {
3030 /* I'm not going to handle 1900 and prior */
3031 /* We'll just be silent on the year, instead of bombing out. */
3032 } else {
3033 res = wait_file(chan, ints, "digits/19", lang);
3034 if (!res) {
3035 if (tm.tm_year <= 9) {
3036 /* 1901 - 1909 */
3037 res = wait_file(chan,ints, "digits/oh", lang);
3040 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3043 break;
3044 case 'I':
3045 case 'l':
3046 /* 12-Hour */
3047 if (tm.tm_hour == 0)
3048 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3049 else if (tm.tm_hour > 12)
3050 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3051 else
3052 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3053 res = wait_file(chan,ints,nextmsg,lang);
3054 break;
3055 case 'H':
3056 case 'k':
3057 /* 24-Hour */
3058 if (format[offset] == 'H') {
3059 /* e.g. oh-eight */
3060 if (tm.tm_hour < 10) {
3061 res = wait_file(chan,ints, "digits/oh",lang);
3063 } else {
3064 /* e.g. eight */
3065 if (tm.tm_hour == 0) {
3066 res = wait_file(chan,ints, "digits/oh",lang);
3069 if (!res) {
3070 if (tm.tm_hour != 0) {
3071 int remainder = tm.tm_hour;
3072 if (tm.tm_hour > 20) {
3073 res = wait_file(chan,ints, "digits/20",lang);
3074 remainder -= 20;
3076 if (!res) {
3077 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3078 res = wait_file(chan,ints,nextmsg,lang);
3082 break;
3083 case 'M':
3084 case 'N':
3085 /* Minute */
3086 if (tm.tm_min == 0) {
3087 if (format[offset] == 'M') {
3088 res = wait_file(chan, ints, "digits/oclock", lang);
3089 } else {
3090 res = wait_file(chan, ints, "digits/hundred", lang);
3092 } else if (tm.tm_min < 10) {
3093 res = wait_file(chan,ints, "digits/oh",lang);
3094 if (!res) {
3095 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3096 res = wait_file(chan,ints,nextmsg,lang);
3098 } else {
3099 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3101 break;
3102 case 'P':
3103 case 'p':
3104 /* AM/PM */
3105 if (tm.tm_hour > 11)
3106 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3107 else
3108 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3109 res = wait_file(chan,ints,nextmsg,lang);
3110 break;
3111 case 'Q':
3112 /* Shorthand for "Today", "Yesterday", or ABdY */
3113 /* XXX As emphasized elsewhere, this should the native way in your
3114 * language to say the date, with changes in what you say, depending
3115 * upon how recent the date is. XXX */
3117 struct timeval now;
3118 struct tm tmnow;
3119 time_t beg_today, tt;
3121 gettimeofday(&now,NULL);
3122 tt = now.tv_sec;
3123 ast_localtime(&tt,&tmnow,timezone);
3124 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3125 /* In any case, it saves not having to do ast_mktime() */
3126 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3127 if (beg_today < time) {
3128 /* Today */
3129 res = wait_file(chan,ints, "digits/today",lang);
3130 } else if (beg_today - 86400 < time) {
3131 /* Yesterday */
3132 res = wait_file(chan,ints, "digits/yesterday",lang);
3133 } else if (beg_today - 86400 * 6 < time) {
3134 /* Within the last week */
3135 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3136 } else if (beg_today - 2628000 < time) {
3137 /* Less than a month ago - "Sunday, October third" */
3138 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3139 } else if (beg_today - 15768000 < time) {
3140 /* Less than 6 months ago - "August seventh" */
3141 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3142 } else {
3143 /* More than 6 months ago - "April nineteenth two thousand three" */
3144 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3147 break;
3148 case 'q':
3149 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3150 /* XXX As emphasized elsewhere, this should the native way in your
3151 * language to say the date, with changes in what you say, depending
3152 * upon how recent the date is. XXX */
3154 struct timeval now;
3155 struct tm tmnow;
3156 time_t beg_today, tt;
3158 gettimeofday(&now,NULL);
3159 tt = now.tv_sec;
3160 ast_localtime(&tt,&tmnow,timezone);
3161 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3162 /* In any case, it saves not having to do ast_mktime() */
3163 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3164 if (beg_today < time) {
3165 /* Today */
3166 } else if ((beg_today - 86400) < time) {
3167 /* Yesterday */
3168 res = wait_file(chan,ints, "digits/yesterday",lang);
3169 } else if (beg_today - 86400 * 6 < time) {
3170 /* Within the last week */
3171 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3172 } else if (beg_today - 2628000 < time) {
3173 /* Less than a month ago - "Sunday, October third" */
3174 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3175 } else if (beg_today - 15768000 < time) {
3176 /* Less than 6 months ago - "August seventh" */
3177 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3178 } else {
3179 /* More than 6 months ago - "April nineteenth two thousand three" */
3180 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3183 break;
3184 case 'R':
3185 res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
3186 break;
3187 case 'S':
3188 /* Seconds */
3189 if (tm.tm_sec == 0) {
3190 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3191 res = wait_file(chan,ints,nextmsg,lang);
3192 } else if (tm.tm_sec < 10) {
3193 res = wait_file(chan,ints, "digits/oh",lang);
3194 if (!res) {
3195 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3196 res = wait_file(chan,ints,nextmsg,lang);
3198 } else {
3199 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
3201 break;
3202 case 'T':
3203 res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
3204 break;
3205 case ' ':
3206 case ' ':
3207 /* Just ignore spaces and tabs */
3208 break;
3209 default:
3210 /* Unknown character */
3211 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3213 /* Jump out on DTMF */
3214 if (res) {
3215 break;
3218 return res;
3221 /* Danish syntax */
3222 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)
3224 struct tm tm;
3225 int res=0, offset, sndoffset;
3226 char sndfile[256], nextmsg[256];
3228 if (!format)
3229 format = "A dBY HMS";
3231 ast_localtime(&time,&tm,timezone);
3233 for (offset=0 ; format[offset] != '\0' ; offset++) {
3234 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3235 switch (format[offset]) {
3236 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3237 case '\'':
3238 /* Literal name of a sound file */
3239 sndoffset=0;
3240 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3241 sndfile[sndoffset] = format[offset];
3242 sndfile[sndoffset] = '\0';
3243 res = wait_file(chan,ints,sndfile,lang);
3244 break;
3245 case 'A':
3246 case 'a':
3247 /* Sunday - Saturday */
3248 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3249 res = wait_file(chan,ints,nextmsg,lang);
3250 break;
3251 case 'B':
3252 case 'b':
3253 case 'h':
3254 /* January - December */
3255 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3256 res = wait_file(chan,ints,nextmsg,lang);
3257 break;
3258 case 'm':
3259 /* Month enumerated */
3260 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3261 break;
3262 case 'd':
3263 case 'e':
3264 /* First - Thirtyfirst */
3265 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3266 break;
3267 case 'Y':
3268 /* Year */
3270 int year = tm.tm_year + 1900;
3271 if (year > 1999) { /* year 2000 and later */
3272 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3273 } else {
3274 if (year < 1100) {
3275 /* I'm not going to handle 1100 and prior */
3276 /* We'll just be silent on the year, instead of bombing out. */
3277 } else {
3278 /* year 1100 to 1999. will anybody need this?!? */
3279 /* say 1967 as 'nineteen hundred seven and sixty' */
3280 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3281 res = wait_file(chan,ints,nextmsg,lang);
3282 if (!res) {
3283 res = wait_file(chan,ints, "digits/hundred",lang);
3284 if (!res && year % 100 != 0) {
3285 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3291 break;
3292 case 'I':
3293 case 'l':
3294 /* 12-Hour */
3295 res = wait_file(chan,ints,"digits/oclock",lang);
3296 if (tm.tm_hour == 0)
3297 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3298 else if (tm.tm_hour > 12)
3299 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3300 else
3301 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3302 if (!res) {
3303 res = wait_file(chan,ints,nextmsg,lang);
3305 break;
3306 case 'H':
3307 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3308 if (tm.tm_hour < 10 && tm.tm_hour > 0) {
3309 res = wait_file(chan,ints, "digits/0",lang);
3311 /* FALLTRHU */
3312 case 'k':
3313 /* 24-Hour */
3314 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3315 break;
3316 case 'M':
3317 /* Minute */
3318 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3319 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3321 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3322 if (tm.tm_min == 1) {
3323 res = wait_file(chan,ints,"digits/minute",lang);
3324 } else {
3325 res = wait_file(chan,ints,"digits/minutes",lang);
3328 break;
3329 case 'P':
3330 case 'p':
3331 /* AM/PM */
3332 if (tm.tm_hour > 11)
3333 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3334 else
3335 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3336 res = wait_file(chan,ints,nextmsg,lang);
3337 break;
3338 case 'Q':
3339 /* Shorthand for "Today", "Yesterday", or AdBY */
3340 /* XXX As emphasized elsewhere, this should the native way in your
3341 * language to say the date, with changes in what you say, depending
3342 * upon how recent the date is. XXX */
3344 struct timeval now;
3345 struct tm tmnow;
3346 time_t beg_today, tt;
3348 gettimeofday(&now,NULL);
3349 tt = now.tv_sec;
3350 ast_localtime(&tt,&tmnow,timezone);
3351 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3352 /* In any case, it saves not having to do ast_mktime() */
3353 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3354 if (beg_today < time) {
3355 /* Today */
3356 res = wait_file(chan,ints, "digits/today",lang);
3357 } else if (beg_today - 86400 < time) {
3358 /* Yesterday */
3359 res = wait_file(chan,ints, "digits/yesterday",lang);
3360 } else {
3361 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3364 break;
3365 case 'q':
3366 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3367 /* XXX As emphasized elsewhere, this should the native way in your
3368 * language to say the date, with changes in what you say, depending
3369 * upon how recent the date is. XXX */
3371 struct timeval now;
3372 struct tm tmnow;
3373 time_t beg_today, tt;
3375 gettimeofday(&now,NULL);
3376 tt = now.tv_sec;
3377 ast_localtime(&tt,&tmnow,timezone);
3378 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3379 /* In any case, it saves not having to do ast_mktime() */
3380 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3381 if (beg_today < time) {
3382 /* Today */
3383 } else if ((beg_today - 86400) < time) {
3384 /* Yesterday */
3385 res = wait_file(chan,ints, "digits/yesterday",lang);
3386 } else if (beg_today - 86400 * 6 < time) {
3387 /* Within the last week */
3388 res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
3389 } else {
3390 res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
3393 break;
3394 case 'R':
3395 res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
3396 break;
3397 case 'S':
3398 /* Seconds */
3399 res = wait_file(chan,ints, "digits/and",lang);
3400 if (!res) {
3401 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3402 if (!res) {
3403 res = wait_file(chan,ints, "digits/seconds",lang);
3406 break;
3407 case 'T':
3408 res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
3409 break;
3410 case ' ':
3411 case ' ':
3412 /* Just ignore spaces and tabs */
3413 break;
3414 default:
3415 /* Unknown character */
3416 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3418 /* Jump out on DTMF */
3419 if (res) {
3420 break;
3423 return res;
3426 /* German syntax */
3427 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)
3429 struct tm tm;
3430 int res=0, offset, sndoffset;
3431 char sndfile[256], nextmsg[256];
3433 if (!format)
3434 format = "A dBY HMS";
3436 ast_localtime(&time,&tm,timezone);
3438 for (offset=0 ; format[offset] != '\0' ; offset++) {
3439 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3440 switch (format[offset]) {
3441 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3442 case '\'':
3443 /* Literal name of a sound file */
3444 sndoffset=0;
3445 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3446 sndfile[sndoffset] = format[offset];
3447 sndfile[sndoffset] = '\0';
3448 res = wait_file(chan,ints,sndfile,lang);
3449 break;
3450 case 'A':
3451 case 'a':
3452 /* Sunday - Saturday */
3453 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3454 res = wait_file(chan,ints,nextmsg,lang);
3455 break;
3456 case 'B':
3457 case 'b':
3458 case 'h':
3459 /* January - December */
3460 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3461 res = wait_file(chan,ints,nextmsg,lang);
3462 break;
3463 case 'm':
3464 /* Month enumerated */
3465 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
3466 break;
3467 case 'd':
3468 case 'e':
3469 /* First - Thirtyfirst */
3470 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
3471 break;
3472 case 'Y':
3473 /* Year */
3475 int year = tm.tm_year + 1900;
3476 if (year > 1999) { /* year 2000 and later */
3477 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3478 } else {
3479 if (year < 1100) {
3480 /* I'm not going to handle 1100 and prior */
3481 /* We'll just be silent on the year, instead of bombing out. */
3482 } else {
3483 /* year 1100 to 1999. will anybody need this?!? */
3484 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3485 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
3486 res = wait_file(chan,ints,nextmsg,lang);
3487 if (!res) {
3488 res = wait_file(chan,ints, "digits/hundred",lang);
3489 if (!res && year % 100 != 0) {
3490 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3496 break;
3497 case 'I':
3498 case 'l':
3499 /* 12-Hour */
3500 if (tm.tm_hour == 0)
3501 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3502 else if (tm.tm_hour > 12)
3503 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3504 else
3505 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3506 res = wait_file(chan,ints,nextmsg,lang);
3507 if (!res) {
3508 res = wait_file(chan,ints,"digits/oclock",lang);
3510 break;
3511 case 'H':
3512 case 'k':
3513 /* 24-Hour */
3514 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
3515 if (!res) {
3516 res = wait_file(chan,ints,"digits/oclock",lang);
3518 break;
3519 case 'M':
3520 /* Minute */
3521 if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3522 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
3524 if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3525 if (tm.tm_min == 1) {
3526 res = wait_file(chan,ints,"digits/minute",lang);
3527 } else {
3528 res = wait_file(chan,ints,"digits/minutes",lang);
3531 break;
3532 case 'P':
3533 case 'p':
3534 /* AM/PM */
3535 if (tm.tm_hour > 11)
3536 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3537 else
3538 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3539 res = wait_file(chan,ints,nextmsg,lang);
3540 break;
3541 case 'Q':
3542 /* Shorthand for "Today", "Yesterday", or AdBY */
3543 /* XXX As emphasized elsewhere, this should the native way in your
3544 * language to say the date, with changes in what you say, depending
3545 * upon how recent the date is. XXX */
3547 struct timeval now;
3548 struct tm tmnow;
3549 time_t beg_today, tt;
3551 gettimeofday(&now,NULL);
3552 tt = now.tv_sec;
3553 ast_localtime(&tt,&tmnow,timezone);
3554 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3555 /* In any case, it saves not having to do ast_mktime() */
3556 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3557 if (beg_today < time) {
3558 /* Today */
3559 res = wait_file(chan,ints, "digits/today",lang);
3560 } else if (beg_today - 86400 < time) {
3561 /* Yesterday */
3562 res = wait_file(chan,ints, "digits/yesterday",lang);
3563 } else {
3564 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3567 break;
3568 case 'q':
3569 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3570 /* XXX As emphasized elsewhere, this should the native way in your
3571 * language to say the date, with changes in what you say, depending
3572 * upon how recent the date is. XXX */
3574 struct timeval now;
3575 struct tm tmnow;
3576 time_t beg_today, tt;
3578 gettimeofday(&now,NULL);
3579 tt = now.tv_sec;
3580 ast_localtime(&tt,&tmnow,timezone);
3581 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3582 /* In any case, it saves not having to do ast_mktime() */
3583 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3584 if (beg_today < time) {
3585 /* Today */
3586 } else if ((beg_today - 86400) < time) {
3587 /* Yesterday */
3588 res = wait_file(chan,ints, "digits/yesterday",lang);
3589 } else if (beg_today - 86400 * 6 < time) {
3590 /* Within the last week */
3591 res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
3592 } else {
3593 res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
3596 break;
3597 case 'R':
3598 res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
3599 break;
3600 case 'S':
3601 /* Seconds */
3602 res = wait_file(chan,ints, "digits/and",lang);
3603 if (!res) {
3604 res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
3605 if (!res) {
3606 res = wait_file(chan,ints, "digits/seconds",lang);
3609 break;
3610 case 'T':
3611 res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
3612 break;
3613 case ' ':
3614 case ' ':
3615 /* Just ignore spaces and tabs */
3616 break;
3617 default:
3618 /* Unknown character */
3619 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3621 /* Jump out on DTMF */
3622 if (res) {
3623 break;
3626 return res;
3629 /* TODO: this probably is not the correct format for doxygen remarks */
3631 /** ast_say_date_with_format_he Say formmated date in Hebrew
3633 * \ref ast_say_date_with_format_en for the details of the options
3635 * Changes from the English version:
3637 * * don't replicate in here the logic of ast_say_number_full_he
3639 * * year is always 4-digit (because it's simpler)
3641 * * added c, x, and X. Mainly for my tests
3643 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3645 * TODO:
3646 * * A "ha" is missing in the standard date format, before the 'd'.
3647 * * The numbers of 3000--19000 are not handled well
3649 #define IL_DATE_STR "AdBY"
3650 #define IL_TIME_STR "IMp"
3651 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3652 int ast_say_date_with_format_he(struct ast_channel *chan, time_t time,
3653 const char *ints, const char *lang, const char *format,
3654 const char *timezone)
3656 /* TODO: This whole function is cut&paste from
3657 * ast_say_date_with_format_en . Is that considered acceptable?
3659 struct tm tm;
3660 int res=0, offset, sndoffset;
3661 char sndfile[256], nextmsg[256];
3663 if (!format)
3664 format = IL_DATE_STR_FULL;
3666 ast_localtime(&time,&tm,timezone);
3668 for (offset=0 ; format[offset] != '\0' ; offset++) {
3669 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3670 switch (format[offset]) {
3671 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3672 case '\'':
3673 /* Literal name of a sound file */
3674 sndoffset=0;
3675 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3676 sndfile[sndoffset] = format[offset];
3677 sndfile[sndoffset] = '\0';
3678 res = wait_file(chan,ints,sndfile,lang);
3679 break;
3680 case 'A':
3681 case 'a':
3682 /* Sunday - Saturday */
3683 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3684 res = wait_file(chan,ints,nextmsg,lang);
3685 break;
3686 case 'B':
3687 case 'b':
3688 case 'h':
3689 /* January - December */
3690 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3691 res = wait_file(chan,ints,nextmsg,lang);
3692 break;
3693 case 'd':
3694 case 'e': /* Day of the month */
3695 /* I'm not sure exactly what the parameters
3696 * audiofd and ctrlfd to
3697 * ast_say_number_full_he mean, but it seems
3698 * safe to pass -1 there.
3700 * At least in one of the pathes :-(
3702 res = ast_say_number_full_he(chan, tm.tm_mday,
3703 ints, lang, "m", -1, -1
3705 break;
3706 case 'Y': /* Year */
3707 res = ast_say_number_full_he(chan, tm.tm_year+1900,
3708 ints, lang, "f", -1, -1
3710 break;
3711 case 'I':
3712 case 'l': /* 12-Hour */
3714 int hour = tm.tm_hour;
3715 hour = hour%12;
3716 if (hour == 0) hour=12;
3718 res = ast_say_number_full_he(chan, hour,
3719 ints, lang, "f", -1, -1
3722 break;
3723 case 'H':
3724 case 'k': /* 24-Hour */
3725 /* With 'H' there is an 'oh' after a single-
3726 * digit hour */
3727 if ((format[offset] == 'H') &&
3728 (tm.tm_hour <10)&&(tm.tm_hour>0)
3729 ) { /* e.g. oh-eight */
3730 res = wait_file(chan,ints, "digits/oh",lang);
3733 res = ast_say_number_full_he(chan, tm.tm_hour,
3734 ints, lang, "f", -1, -1
3736 break;
3737 case 'M': /* Minute */
3738 res = ast_say_number_full_he(chan, tm.tm_min,
3739 ints, lang,"f", -1, -1
3741 break;
3742 case 'P':
3743 case 'p':
3744 /* AM/PM */
3745 if (tm.tm_hour > 11)
3746 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3747 else
3748 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3749 res = wait_file(chan,ints,nextmsg,lang);
3750 break;
3751 case 'Q':
3752 /* Shorthand for "Today", "Yesterday", or "date" */
3753 case 'q':
3754 /* Shorthand for "" (today), "Yesterday", A
3755 * (weekday), or "date" */
3756 /* XXX As emphasized elsewhere, this should the native way in your
3757 * language to say the date, with changes in what you say, depending
3758 * upon how recent the date is. XXX */
3760 struct timeval now;
3761 struct tm tmnow;
3762 time_t beg_today, tt;
3763 char todo = format[offset]; /* The letter to format*/
3765 gettimeofday(&now,NULL);
3766 tt = now.tv_sec;
3767 ast_localtime(&tt,&tmnow,timezone);
3768 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3769 /* In any case, it saves not having to do ast_mktime() */
3770 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3771 if (beg_today < time) {
3772 /* Today */
3773 if (todo == 'Q') {
3774 res = wait_file(chan,
3775 ints,
3776 "digits/today",
3777 lang);
3779 } else if (beg_today - 86400 < time) {
3780 /* Yesterday */
3781 res = wait_file(chan,ints, "digits/yesterday",lang);
3782 } else if ((todo != 'Q') &&
3783 (beg_today - 86400 * 6 < time))
3785 /* Within the last week */
3786 res = ast_say_date_with_format_he(chan,
3787 time, ints, lang,
3788 "A", timezone);
3789 } else {
3790 res = ast_say_date_with_format_he(chan,
3791 time, ints, lang,
3792 IL_DATE_STR, timezone);
3795 break;
3796 case 'R':
3797 res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
3798 break;
3799 case 'S': /* Seconds */
3800 res = ast_say_number_full_he(chan, tm.tm_sec,
3801 ints, lang, "f", -1, -1
3803 break;
3804 case 'T':
3805 res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
3806 break;
3807 /* c, x, and X seem useful for testing. Not sure
3808 * if thiey're good for the general public */
3809 case 'c':
3810 res = ast_say_date_with_format_he(chan, time,
3811 ints, lang, IL_DATE_STR_FULL, timezone);
3812 break;
3813 case 'x':
3814 res = ast_say_date_with_format_he(chan, time,
3815 ints, lang, IL_DATE_STR, timezone);
3816 break;
3817 case 'X': /* Currently not locale-dependent...*/
3818 res = ast_say_date_with_format_he(chan, time,
3819 ints, lang, IL_TIME_STR, timezone);
3820 break;
3821 case ' ':
3822 case ' ':
3823 /* Just ignore spaces and tabs */
3824 break;
3825 default:
3826 /* Unknown character */
3827 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
3829 /* Jump out on DTMF */
3830 if (res) {
3831 break;
3834 return res;
3838 /* Spanish syntax */
3839 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)
3841 struct tm tm;
3842 int res=0, offset, sndoffset;
3843 char sndfile[256], nextmsg[256];
3845 if (format == NULL)
3846 format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3848 ast_localtime(&time,&tm,timezone);
3850 for (offset=0 ; format[offset] != '\0' ; offset++) {
3851 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
3852 switch (format[offset]) {
3853 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3854 case '\'':
3855 /* Literal name of a sound file */
3856 sndoffset=0;
3857 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3858 sndfile[sndoffset] = format[offset];
3859 sndfile[sndoffset] = '\0';
3860 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
3861 res = wait_file(chan,ints,nextmsg,lang);
3862 break;
3863 case 'A':
3864 case 'a':
3865 /* Sunday - Saturday */
3866 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3867 res = wait_file(chan,ints,nextmsg,lang);
3868 break;
3869 case 'B':
3870 case 'b':
3871 case 'h':
3872 /* January - December */
3873 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3874 res = wait_file(chan,ints,nextmsg,lang);
3875 break;
3876 case 'm':
3877 /* First - Twelfth */
3878 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
3879 res = wait_file(chan,ints,nextmsg,lang);
3880 break;
3881 case 'd':
3882 case 'e':
3883 /* First - Thirtyfirst */
3884 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
3885 break;
3886 case 'Y':
3887 /* Year */
3888 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3889 break;
3890 case 'I':
3891 case 'l':
3892 /* 12-Hour */
3893 if (tm.tm_hour == 0)
3894 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3895 else if (tm.tm_hour > 12)
3896 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3897 else
3898 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3899 res = wait_file(chan,ints,nextmsg,lang);
3900 break;
3901 case 'H':
3902 case 'k':
3903 /* 24-Hour */
3904 res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
3905 break;
3906 case 'M':
3907 /* Minute */
3908 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3909 break;
3910 case 'P':
3911 case 'p':
3912 /* AM/PM */
3913 if (tm.tm_hour > 18)
3914 res = wait_file(chan, ints, "digits/p-m", lang);
3915 else if (tm.tm_hour > 12)
3916 res = wait_file(chan, ints, "digits/afternoon", lang);
3917 else if (tm.tm_hour)
3918 res = wait_file(chan, ints, "digits/a-m", lang);
3919 break;
3920 case 'Q':
3921 /* Shorthand for "Today", "Yesterday", or ABdY */
3922 /* XXX As emphasized elsewhere, this should the native way in your
3923 * language to say the date, with changes in what you say, depending
3924 * upon how recent the date is. XXX */
3926 struct timeval now;
3927 struct tm tmnow;
3928 time_t beg_today, tt;
3930 gettimeofday(&now,NULL);
3931 tt = now.tv_sec;
3932 ast_localtime(&tt,&tmnow,timezone);
3933 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3934 /* In any case, it saves not having to do ast_mktime() */
3935 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3936 if (beg_today < time) {
3937 /* Today */
3938 res = wait_file(chan,ints, "digits/today",lang);
3939 } else if (beg_today - 86400 < time) {
3940 /* Yesterday */
3941 res = wait_file(chan,ints, "digits/yesterday",lang);
3942 } else {
3943 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3946 break;
3947 case 'q':
3948 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3949 /* XXX As emphasized elsewhere, this should the native way in your
3950 * language to say the date, with changes in what you say, depending
3951 * upon how recent the date is. XXX */
3953 struct timeval now;
3954 struct tm tmnow;
3955 time_t beg_today, tt;
3957 gettimeofday(&now,NULL);
3958 tt = now.tv_sec;
3959 ast_localtime(&tt,&tmnow,timezone);
3960 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3961 /* In any case, it saves not having to do ast_mktime() */
3962 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3963 if (beg_today < time) {
3964 /* Today */
3965 res = wait_file(chan,ints, "digits/today",lang);
3966 } else if ((beg_today - 86400) < time) {
3967 /* Yesterday */
3968 res = wait_file(chan,ints, "digits/yesterday",lang);
3969 } else if (beg_today - 86400 * 6 < time) {
3970 /* Within the last week */
3971 res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
3972 } else {
3973 res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
3976 break;
3977 case 'R':
3978 res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
3979 break;
3980 case 'S':
3981 /* Seconds */
3982 if (tm.tm_sec == 0) {
3983 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3984 res = wait_file(chan,ints,nextmsg,lang);
3985 } else if (tm.tm_sec < 10) {
3986 res = wait_file(chan,ints, "digits/oh",lang);
3987 if (!res) {
3988 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3989 res = wait_file(chan,ints,nextmsg,lang);
3991 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
3992 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
3993 res = wait_file(chan,ints,nextmsg,lang);
3994 } else {
3995 int ten, one;
3996 ten = (tm.tm_sec / 10) * 10;
3997 one = (tm.tm_sec % 10);
3998 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
3999 res = wait_file(chan,ints,nextmsg,lang);
4000 if (!res) {
4001 /* Fifty, not fifty-zero */
4002 if (one != 0) {
4003 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4004 res = wait_file(chan,ints,nextmsg,lang);
4008 break;
4009 case 'T':
4010 res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
4011 break;
4012 case ' ':
4013 case ' ':
4014 /* Just ignore spaces and tabs */
4015 break;
4016 default:
4017 /* Unknown character */
4018 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4020 /* Jump out on DTMF */
4021 if (res) {
4022 break;
4025 return res;
4028 /* French syntax
4029 oclock = heure
4031 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)
4033 struct tm tm;
4034 int res=0, offset, sndoffset;
4035 char sndfile[256], nextmsg[256];
4037 if (format == NULL)
4038 format = "AdBY 'digits/at' IMp";
4040 ast_localtime(&time,&tm,timezone);
4042 for (offset=0 ; format[offset] != '\0' ; offset++) {
4043 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4044 switch (format[offset]) {
4045 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4046 case '\'':
4047 /* Literal name of a sound file */
4048 sndoffset=0;
4049 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4050 sndfile[sndoffset] = format[offset];
4051 sndfile[sndoffset] = '\0';
4052 res = wait_file(chan,ints,sndfile,lang);
4053 break;
4054 case 'A':
4055 case 'a':
4056 /* Sunday - Saturday */
4057 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4058 res = wait_file(chan,ints,nextmsg,lang);
4059 break;
4060 case 'B':
4061 case 'b':
4062 case 'h':
4063 /* January - December */
4064 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4065 res = wait_file(chan,ints,nextmsg,lang);
4066 break;
4067 case 'm':
4068 /* First - Twelfth */
4069 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4070 res = wait_file(chan,ints,nextmsg,lang);
4071 break;
4072 case 'd':
4073 case 'e':
4074 /* First */
4075 if (tm.tm_mday == 1) {
4076 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4077 res = wait_file(chan,ints,nextmsg,lang);
4078 } else {
4079 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4081 break;
4082 case 'Y':
4083 /* Year */
4084 if (tm.tm_year > 99) {
4085 res = wait_file(chan,ints, "digits/2",lang);
4086 if (!res) {
4087 res = wait_file(chan,ints, "digits/thousand",lang);
4089 if (tm.tm_year > 100) {
4090 if (!res) {
4091 res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
4094 } else {
4095 if (tm.tm_year < 1) {
4096 /* I'm not going to handle 1900 and prior */
4097 /* We'll just be silent on the year, instead of bombing out. */
4098 } else {
4099 res = wait_file(chan,ints, "digits/thousand",lang);
4100 if (!res) {
4101 wait_file(chan,ints, "digits/9",lang);
4102 wait_file(chan,ints, "digits/hundred",lang);
4103 res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
4107 break;
4108 case 'I':
4109 case 'l':
4110 /* 12-Hour */
4111 if (tm.tm_hour == 0)
4112 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4113 else if (tm.tm_hour > 12)
4114 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4115 else
4116 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4117 res = wait_file(chan,ints,nextmsg,lang);
4118 if (!res)
4119 res = wait_file(chan,ints, "digits/oclock",lang);
4120 break;
4121 case 'H':
4122 case 'k':
4123 /* 24-Hour */
4124 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4125 if (!res)
4126 res = wait_file(chan,ints, "digits/oclock",lang);
4127 break;
4128 case 'M':
4129 /* Minute */
4130 if (tm.tm_min == 0) {
4131 break;
4133 res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
4134 break;
4135 case 'P':
4136 case 'p':
4137 /* AM/PM */
4138 if (tm.tm_hour > 11)
4139 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4140 else
4141 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4142 res = wait_file(chan,ints,nextmsg,lang);
4143 break;
4144 case 'Q':
4145 /* Shorthand for "Today", "Yesterday", or AdBY */
4146 /* XXX As emphasized elsewhere, this should the native way in your
4147 * language to say the date, with changes in what you say, depending
4148 * upon how recent the date is. XXX */
4150 struct timeval now;
4151 struct tm tmnow;
4152 time_t beg_today, tt;
4154 gettimeofday(&now,NULL);
4155 tt = now.tv_sec;
4156 ast_localtime(&tt,&tmnow,timezone);
4157 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4158 /* In any case, it saves not having to do ast_mktime() */
4159 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4160 if (beg_today < time) {
4161 /* Today */
4162 res = wait_file(chan,ints, "digits/today",lang);
4163 } else if (beg_today - 86400 < time) {
4164 /* Yesterday */
4165 res = wait_file(chan,ints, "digits/yesterday",lang);
4166 } else {
4167 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4170 break;
4171 case 'q':
4172 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4173 /* XXX As emphasized elsewhere, this should the native way in your
4174 * language to say the date, with changes in what you say, depending
4175 * upon how recent the date is. XXX */
4177 struct timeval now;
4178 struct tm tmnow;
4179 time_t beg_today, tt;
4181 gettimeofday(&now,NULL);
4182 tt = now.tv_sec;
4183 ast_localtime(&tt,&tmnow,timezone);
4184 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4185 /* In any case, it saves not having to do ast_mktime() */
4186 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4187 if (beg_today < time) {
4188 /* Today */
4189 } else if ((beg_today - 86400) < time) {
4190 /* Yesterday */
4191 res = wait_file(chan,ints, "digits/yesterday",lang);
4192 } else if (beg_today - 86400 * 6 < time) {
4193 /* Within the last week */
4194 res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
4195 } else {
4196 res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
4199 break;
4200 case 'R':
4201 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
4202 break;
4203 case 'S':
4204 /* Seconds */
4205 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
4206 if (!res) {
4207 res = wait_file(chan,ints, "digits/second",lang);
4209 break;
4210 case 'T':
4211 res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
4212 break;
4213 case ' ':
4214 case ' ':
4215 /* Just ignore spaces and tabs */
4216 break;
4217 default:
4218 /* Unknown character */
4219 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4221 /* Jump out on DTMF */
4222 if (res) {
4223 break;
4226 return res;
4229 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)
4231 struct tm tm;
4232 int res=0, offset, sndoffset;
4233 char sndfile[256], nextmsg[256];
4235 if (format == NULL)
4236 format = "AdB 'digits/at' IMp";
4238 ast_localtime(&time,&tm,timezone);
4240 for (offset=0 ; format[offset] != '\0' ; offset++) {
4241 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4242 switch (format[offset]) {
4243 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4244 case '\'':
4245 /* Literal name of a sound file */
4246 sndoffset=0;
4247 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4248 sndfile[sndoffset] = format[offset];
4249 sndfile[sndoffset] = '\0';
4250 res = wait_file(chan,ints,sndfile,lang);
4251 break;
4252 case 'A':
4253 case 'a':
4254 /* Sunday - Saturday */
4255 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4256 res = wait_file(chan,ints,nextmsg,lang);
4257 break;
4258 case 'B':
4259 case 'b':
4260 case 'h':
4261 /* January - December */
4262 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4263 res = wait_file(chan,ints,nextmsg,lang);
4264 break;
4265 case 'm':
4266 /* First - Twelfth */
4267 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4268 res = wait_file(chan,ints,nextmsg,lang);
4269 break;
4270 case 'd':
4271 case 'e':
4272 /* First day of the month is spelled as ordinal */
4273 if (tm.tm_mday == 1) {
4274 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
4275 res = wait_file(chan,ints,nextmsg,lang);
4276 } else {
4277 if (!res) {
4278 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4281 break;
4282 case 'Y':
4283 /* Year */
4284 if (tm.tm_year > 99) {
4285 res = wait_file(chan,ints, "digits/ore-2000",lang);
4286 if (tm.tm_year > 100) {
4287 if (!res) {
4288 /* This works until the end of 2021 */
4289 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4290 res = wait_file(chan,ints,nextmsg,lang);
4293 } else {
4294 if (tm.tm_year < 1) {
4295 /* I'm not going to handle 1900 and prior */
4296 /* We'll just be silent on the year, instead of bombing out. */
4297 } else {
4298 res = wait_file(chan,ints, "digits/ore-1900",lang);
4299 if ((!res) && (tm.tm_year != 0)) {
4300 if (tm.tm_year <= 21) {
4301 /* 1910 - 1921 */
4302 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4303 res = wait_file(chan,ints,nextmsg,lang);
4304 } else {
4305 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4306 int ten, one;
4307 ten = tm.tm_year / 10;
4308 one = tm.tm_year % 10;
4309 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4310 res = wait_file(chan,ints,nextmsg,lang);
4311 if (!res) {
4312 if (one != 0) {
4313 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4314 res = wait_file(chan,ints,nextmsg,lang);
4321 break;
4322 case 'I':
4323 case 'l':
4324 /* 12-Hour */
4325 if (tm.tm_hour == 0)
4326 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4327 else if (tm.tm_hour > 12)
4328 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4329 else
4330 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4331 res = wait_file(chan,ints,nextmsg,lang);
4332 break;
4333 case 'H':
4334 case 'k':
4335 /* 24-Hour */
4336 if (tm.tm_hour == 0) {
4337 res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
4338 } else if (tm.tm_hour == 1) {
4339 res = wait_file(chan,ints, "digits/ore-una",lang);
4340 } else {
4341 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4343 break;
4344 case 'M':
4345 /* Minute */
4346 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4347 break;
4348 case 'P':
4349 case 'p':
4350 /* AM/PM */
4351 if (tm.tm_hour > 11)
4352 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4353 else
4354 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4355 res = wait_file(chan,ints,nextmsg,lang);
4356 break;
4357 case 'Q':
4358 /* Shorthand for "Today", "Yesterday", or ABdY */
4359 /* XXX As emphasized elsewhere, this should the native way in your
4360 * language to say the date, with changes in what you say, depending
4361 * upon how recent the date is. XXX */
4363 struct timeval now;
4364 struct tm tmnow;
4365 time_t beg_today, tt;
4367 gettimeofday(&now,NULL);
4368 tt = now.tv_sec;
4369 ast_localtime(&tt,&tmnow,timezone);
4370 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4371 /* In any case, it saves not having to do ast_mktime() */
4372 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4373 if (beg_today < time) {
4374 /* Today */
4375 res = wait_file(chan,ints, "digits/today",lang);
4376 } else if (beg_today - 86400 < time) {
4377 /* Yesterday */
4378 res = wait_file(chan,ints, "digits/yesterday",lang);
4379 } else {
4380 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4383 break;
4384 case 'q':
4385 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4387 struct timeval now;
4388 struct tm tmnow;
4389 time_t beg_today, tt;
4391 gettimeofday(&now,NULL);
4392 tt = now.tv_sec;
4393 ast_localtime(&tt,&tmnow,timezone);
4394 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4395 /* In any case, it saves not having to do ast_mktime() */
4396 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4397 if (beg_today < time) {
4398 /* Today */
4399 } else if ((beg_today - 86400) < time) {
4400 /* Yesterday */
4401 res = wait_file(chan,ints, "digits/yesterday",lang);
4402 } else if (beg_today - 86400 * 6 < time) {
4403 /* Within the last week */
4404 res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
4405 } else {
4406 res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
4409 break;
4410 case 'R':
4411 res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
4412 break;
4413 case 'S':
4414 /* Seconds */
4415 if (tm.tm_sec == 0) {
4416 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4417 res = wait_file(chan,ints,nextmsg,lang);
4418 } else if (tm.tm_sec < 10) {
4419 res = wait_file(chan,ints, "digits/oh",lang);
4420 if (!res) {
4421 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4422 res = wait_file(chan,ints,nextmsg,lang);
4424 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
4425 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
4426 res = wait_file(chan,ints,nextmsg,lang);
4427 } else {
4428 int ten, one;
4429 ten = (tm.tm_sec / 10) * 10;
4430 one = (tm.tm_sec % 10);
4431 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
4432 res = wait_file(chan,ints,nextmsg,lang);
4433 if (!res) {
4434 /* Fifty, not fifty-zero */
4435 if (one != 0) {
4436 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4437 res = wait_file(chan,ints,nextmsg,lang);
4441 break;
4442 case 'T':
4443 res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
4444 break;
4445 case ' ':
4446 case ' ':
4447 /* Just ignore spaces and tabs */
4448 break;
4449 default:
4450 /* Unknown character */
4451 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4453 /* Jump out on DTMF */
4454 if (res) {
4455 break;
4458 return res;
4461 /* Dutch syntax */
4462 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)
4464 struct tm tm;
4465 int res=0, offset, sndoffset;
4466 char sndfile[256], nextmsg[256];
4468 if (format == NULL)
4469 format = "ABdY 'digits/at' IMp";
4471 ast_localtime(&time,&tm,timezone);
4473 for (offset=0 ; format[offset] != '\0' ; offset++) {
4474 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4475 switch (format[offset]) {
4476 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4477 case '\'':
4478 /* Literal name of a sound file */
4479 sndoffset=0;
4480 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4481 sndfile[sndoffset] = format[offset];
4482 sndfile[sndoffset] = '\0';
4483 res = wait_file(chan,ints,sndfile,lang);
4484 break;
4485 case 'A':
4486 case 'a':
4487 /* Sunday - Saturday */
4488 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4489 res = wait_file(chan,ints,nextmsg,lang);
4490 break;
4491 case 'B':
4492 case 'b':
4493 case 'h':
4494 /* January - December */
4495 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4496 res = wait_file(chan,ints,nextmsg,lang);
4497 break;
4498 case 'm':
4499 /* First - Twelfth */
4500 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4501 res = wait_file(chan,ints,nextmsg,lang);
4502 break;
4503 case 'd':
4504 case 'e':
4505 /* First - Thirtyfirst */
4506 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
4507 break;
4508 case 'Y':
4509 /* Year */
4510 if (tm.tm_year > 99) {
4511 res = wait_file(chan,ints, "digits/2",lang);
4512 if (!res) {
4513 res = wait_file(chan,ints, "digits/thousand",lang);
4515 if (tm.tm_year > 100) {
4516 if (!res) {
4517 /* This works until the end of 2020 */
4518 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
4519 res = wait_file(chan,ints,nextmsg,lang);
4522 } else {
4523 if (tm.tm_year < 1) {
4524 /* I'm not going to handle 1900 and prior */
4525 /* We'll just be silent on the year, instead of bombing out. */
4526 } else {
4527 res = wait_file(chan,ints, "digits/19",lang);
4528 if (!res) {
4529 if (tm.tm_year <= 9) {
4530 /* 1901 - 1909 */
4531 res = wait_file(chan,ints, "digits/oh",lang);
4532 if (!res) {
4533 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4534 res = wait_file(chan,ints,nextmsg,lang);
4536 } else if (tm.tm_year <= 20) {
4537 /* 1910 - 1920 */
4538 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
4539 res = wait_file(chan,ints,nextmsg,lang);
4540 } else {
4541 /* 1921 - 1999 */
4542 int ten, one;
4543 ten = tm.tm_year / 10;
4544 one = tm.tm_year % 10;
4545 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
4546 res = wait_file(chan,ints,nextmsg,lang);
4547 if (!res) {
4548 if (one != 0) {
4549 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
4550 res = wait_file(chan,ints,nextmsg,lang);
4557 break;
4558 case 'I':
4559 case 'l':
4560 /* 12-Hour */
4561 if (tm.tm_hour == 0)
4562 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
4563 else if (tm.tm_hour > 12)
4564 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4565 else
4566 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
4567 res = wait_file(chan,ints,nextmsg,lang);
4568 break;
4569 case 'H':
4570 case 'k':
4571 /* 24-Hour */
4572 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4573 if (!res) {
4574 res = wait_file(chan,ints, "digits/nl-uur",lang);
4576 break;
4577 case 'M':
4578 /* Minute */
4579 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4580 break;
4581 case 'P':
4582 case 'p':
4583 /* AM/PM */
4584 if (tm.tm_hour > 11)
4585 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
4586 else
4587 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
4588 res = wait_file(chan,ints,nextmsg,lang);
4589 break;
4590 case 'Q':
4591 /* Shorthand for "Today", "Yesterday", or ABdY */
4592 /* XXX As emphasized elsewhere, this should the native way in your
4593 * language to say the date, with changes in what you say, depending
4594 * upon how recent the date is. XXX */
4596 struct timeval now;
4597 struct tm tmnow;
4598 time_t beg_today, tt;
4600 gettimeofday(&now,NULL);
4601 tt = now.tv_sec;
4602 ast_localtime(&tt,&tmnow,timezone);
4603 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4604 /* In any case, it saves not having to do ast_mktime() */
4605 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4606 if (beg_today < time) {
4607 /* Today */
4608 res = wait_file(chan,ints, "digits/today",lang);
4609 } else if (beg_today - 86400 < time) {
4610 /* Yesterday */
4611 res = wait_file(chan,ints, "digits/yesterday",lang);
4612 } else {
4613 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4616 break;
4617 case 'q':
4618 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4620 struct timeval now;
4621 struct tm tmnow;
4622 time_t beg_today, tt;
4624 gettimeofday(&now,NULL);
4625 tt = now.tv_sec;
4626 ast_localtime(&tt,&tmnow,timezone);
4627 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4628 /* In any case, it saves not having to do ast_mktime() */
4629 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4630 if (beg_today < time) {
4631 /* Today */
4632 } else if ((beg_today - 86400) < time) {
4633 /* Yesterday */
4634 res = wait_file(chan,ints, "digits/yesterday",lang);
4635 } else if (beg_today - 86400 * 6 < time) {
4636 /* Within the last week */
4637 res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
4638 } else {
4639 res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
4642 break;
4643 case 'R':
4644 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
4645 break;
4646 case 'S':
4647 /* Seconds */
4648 res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4649 break;
4650 case 'T':
4651 res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
4652 break;
4653 case ' ':
4654 case ' ':
4655 /* Just ignore spaces and tabs */
4656 break;
4657 default:
4658 /* Unknown character */
4659 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4661 /* Jump out on DTMF */
4662 if (res) {
4663 break;
4666 return res;
4669 /* Polish syntax */
4670 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)
4672 struct tm tm;
4673 int res=0, offset, sndoffset;
4674 char sndfile[256], nextmsg[256];
4676 ast_localtime(&thetime, &tm, timezone);
4678 for (offset = 0 ; format[offset] != '\0' ; offset++) {
4679 int remainder;
4680 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4681 switch (format[offset]) {
4682 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4683 case '\'':
4684 /* Literal name of a sound file */
4685 sndoffset = 0;
4686 for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4687 sndfile[sndoffset] = format[offset];
4688 sndfile[sndoffset] = '\0';
4689 res = wait_file(chan, ints, sndfile, lang);
4690 break;
4691 case 'A':
4692 case 'a':
4693 /* Sunday - Saturday */
4694 snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4695 res = wait_file(chan, ints, nextmsg, lang);
4696 break;
4697 case 'B':
4698 case 'b':
4699 case 'h':
4700 /* January - December */
4701 snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4702 res = wait_file(chan, ints, nextmsg, lang);
4703 break;
4704 case 'm':
4705 /* Month enumerated */
4706 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
4707 break;
4708 case 'd':
4709 case 'e':
4710 /* First - Thirtyfirst */
4711 remainder = tm.tm_mday;
4712 if (tm.tm_mday > 30) {
4713 res = wait_file(chan, ints, "digits/h-30", lang);
4714 remainder -= 30;
4716 if (tm.tm_mday > 20 && tm.tm_mday < 30) {
4717 res = wait_file(chan, ints, "digits/h-20", lang);
4718 remainder -= 20;
4720 if (!res) {
4721 snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
4722 res = wait_file(chan, ints, nextmsg, lang);
4724 break;
4725 case 'Y':
4726 /* Year */
4727 if (tm.tm_year > 100) {
4728 res = wait_file(chan, ints, "digits/2", lang);
4729 if (!res)
4730 res = wait_file(chan, ints, "digits/1000.2",lang);
4731 if (tm.tm_year > 100) {
4732 if (!res)
4733 res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
4735 } else if (tm.tm_year == 100) {
4736 res = wait_file(chan, ints, "digits/h-2000", lang);
4737 } else {
4738 if (tm.tm_year < 1) {
4739 /* I'm not going to handle 1900 and prior */
4740 /* We'll just be silent on the year, instead of bombing out. */
4741 break;
4742 } else {
4743 res = wait_file(chan, ints, "digits/1000", lang);
4744 if (!res) {
4745 wait_file(chan, ints, "digits/900", lang);
4746 res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
4750 if (!res)
4751 wait_file(chan, ints, "digits/year", lang);
4752 break;
4753 case 'I':
4754 case 'l':
4755 /* 12-Hour */
4756 if (tm.tm_hour == 0)
4757 snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
4758 else if (tm.tm_hour > 12)
4759 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
4760 else
4761 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4763 res = wait_file(chan, ints, nextmsg, lang);
4764 break;
4765 case 'H':
4766 case 'k':
4767 /* 24-Hour */
4768 if (tm.tm_hour != 0) {
4769 snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
4770 res = wait_file(chan, ints, nextmsg, lang);
4771 } else
4772 res = wait_file(chan, ints, "digits/t-24", lang);
4773 break;
4774 case 'M':
4775 case 'N':
4776 /* Minute */
4777 if (tm.tm_min == 0) {
4778 if (format[offset] == 'M') {
4779 res = wait_file(chan, ints, "digits/oclock", lang);
4780 } else {
4781 res = wait_file(chan, ints, "digits/100", lang);
4783 } else
4784 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4785 break;
4786 case 'P':
4787 case 'p':
4788 /* AM/PM */
4789 if (tm.tm_hour > 11)
4790 snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
4791 else
4792 snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
4793 res = wait_file(chan, ints, nextmsg, lang);
4794 break;
4795 case 'Q':
4796 /* Shorthand for "Today", "Yesterday", or AdBY */
4798 time_t tv_sec = time(NULL);
4799 struct tm tmnow;
4800 time_t beg_today;
4802 ast_localtime(&tv_sec,&tmnow, timezone);
4803 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4804 /* In any case, it saves not having to do ast_mktime() */
4805 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4806 if (beg_today < thetime) {
4807 /* Today */
4808 res = wait_file(chan, ints, "digits/today", lang);
4809 } else if (beg_today - 86400 < thetime) {
4810 /* Yesterday */
4811 res = wait_file(chan, ints, "digits/yesterday", lang);
4812 } else {
4813 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4816 break;
4817 case 'q':
4818 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4820 time_t tv_sec = time(NULL);
4821 struct tm tmnow;
4822 time_t beg_today;
4824 ast_localtime(&tv_sec, &tmnow, timezone);
4825 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4826 /* In any case, it saves not having to do ast_mktime() */
4827 beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4828 if (beg_today < thetime) {
4829 /* Today */
4830 } else if ((beg_today - 86400) < thetime) {
4831 /* Yesterday */
4832 res = wait_file(chan, ints, "digits/yesterday", lang);
4833 } else if (beg_today - 86400 * 6 < thetime) {
4834 /* Within the last week */
4835 res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
4836 } else {
4837 res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
4840 break;
4841 case 'R':
4842 res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
4843 break;
4844 case 'S':
4845 /* Seconds */
4846 res = wait_file(chan, ints, "digits/and", lang);
4847 if (!res) {
4848 if (tm.tm_sec == 1) {
4849 res = wait_file(chan, ints, "digits/1z", lang);
4850 if (!res)
4851 res = wait_file(chan, ints, "digits/second-a", lang);
4852 } else {
4853 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4854 if (!res) {
4855 int ten, one;
4856 ten = tm.tm_sec / 10;
4857 one = tm.tm_sec % 10;
4859 if (one > 1 && one < 5 && ten != 1)
4860 res = wait_file(chan,ints, "digits/seconds",lang);
4861 else
4862 res = wait_file(chan,ints, "digits/second",lang);
4866 break;
4867 case 'T':
4868 res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
4869 break;
4870 case ' ':
4871 case ' ':
4872 /* Just ignore spaces and tabs */
4873 break;
4874 default:
4875 /* Unknown character */
4876 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4878 /* Jump out on DTMF */
4879 if (res)
4880 break;
4882 return res;
4885 /* Portuguese syntax */
4886 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)
4888 struct tm tm;
4889 int res=0, offset, sndoffset;
4890 char sndfile[256], nextmsg[256];
4892 if (format == NULL)
4893 format = "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4895 ast_localtime(&time,&tm,timezone);
4897 for (offset=0 ; format[offset] != '\0' ; offset++) {
4898 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4899 switch (format[offset]) {
4900 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4901 case '\'':
4902 /* Literal name of a sound file */
4903 sndoffset=0;
4904 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
4905 sndfile[sndoffset] = format[offset];
4906 sndfile[sndoffset] = '\0';
4907 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
4908 res = wait_file(chan,ints,nextmsg,lang);
4909 break;
4910 case 'A':
4911 case 'a':
4912 /* Sunday - Saturday */
4913 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4914 res = wait_file(chan,ints,nextmsg,lang);
4915 break;
4916 case 'B':
4917 case 'b':
4918 case 'h':
4919 /* January - December */
4920 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4921 res = wait_file(chan,ints,nextmsg,lang);
4922 break;
4923 case 'm':
4924 /* First - Twelfth */
4925 if (!strcasecmp(lang, "pt_BR")) {
4926 res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
4927 } else {
4928 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
4929 res = wait_file(chan,ints,nextmsg,lang);
4931 break;
4932 case 'd':
4933 case 'e':
4934 /* First - Thirtyfirst */
4935 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4936 break;
4937 case 'Y':
4938 /* Year */
4939 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4940 break;
4941 case 'I':
4942 case 'l':
4943 /* 12-Hour */
4944 if (!strcasecmp(lang, "pt_BR")) {
4945 if (tm.tm_hour == 0) {
4946 if (format[offset] == 'I')
4947 res = wait_file(chan, ints, "digits/pt-a", lang);
4948 if (!res)
4949 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4950 } else if (tm.tm_hour == 12) {
4951 if (format[offset] == 'I')
4952 res = wait_file(chan, ints, "digits/pt-ao", lang);
4953 if (!res)
4954 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4955 } else {
4956 if (format[offset] == 'I') {
4957 if ((tm.tm_hour % 12) != 1)
4958 res = wait_file(chan, ints, "digits/pt-as", lang);
4959 else
4960 res = wait_file(chan, ints, "digits/pt-a", lang);
4962 if (!res)
4963 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
4964 if ((!res) && (format[offset] == 'I'))
4965 res = ast_say_date_with_format(chan, time, ints, lang, "P", timezone);
4967 } else {
4968 if (tm.tm_hour == 0) {
4969 if (format[offset] == 'I')
4970 res = wait_file(chan, ints, "digits/pt-ah", lang);
4971 if (!res)
4972 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
4974 else if (tm.tm_hour == 12) {
4975 if (format[offset] == 'I')
4976 res = wait_file(chan, ints, "digits/pt-ao", lang);
4977 if (!res)
4978 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
4980 else {
4981 if (format[offset] == 'I') {
4982 res = wait_file(chan, ints, "digits/pt-ah", lang);
4983 if ((tm.tm_hour % 12) != 1)
4984 if (!res)
4985 res = wait_file(chan, ints, "digits/pt-sss", lang);
4987 if (!res)
4988 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
4991 break;
4992 case 'H':
4993 case 'k':
4994 /* 24-Hour */
4995 if (!strcasecmp(lang, "pt_BR")) {
4996 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
4997 if ((!res) && (format[offset] == 'H')) {
4998 if (tm.tm_hour > 1) {
4999 res = wait_file(chan,ints,"digits/hours",lang);
5000 } else {
5001 res = wait_file(chan,ints,"digits/hour",lang);
5004 } else {
5005 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
5006 if (!res) {
5007 if (tm.tm_hour != 0) {
5008 int remainder = tm.tm_hour;
5009 if (tm.tm_hour > 20) {
5010 res = wait_file(chan,ints, "digits/20",lang);
5011 remainder -= 20;
5013 if (!res) {
5014 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
5015 res = wait_file(chan,ints,nextmsg,lang);
5020 break;
5021 case 'M':
5022 /* Minute */
5023 if (!strcasecmp(lang, "pt_BR")) {
5024 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5025 if (!res) {
5026 if (tm.tm_min > 1) {
5027 res = wait_file(chan,ints,"digits/minutes",lang);
5028 } else {
5029 res = wait_file(chan,ints,"digits/minute",lang);
5032 } else {
5033 if (tm.tm_min == 0) {
5034 res = wait_file(chan, ints, "digits/pt-hora", lang);
5035 if (tm.tm_hour != 1)
5036 if (!res)
5037 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
5038 res = wait_file(chan,ints,"digits/pt-e",lang);
5039 if (!res)
5040 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5043 break;
5044 case 'P':
5045 case 'p':
5046 /* AM/PM */
5047 if (!strcasecmp(lang, "pt_BR")) {
5048 if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
5049 res = wait_file(chan, ints, "digits/pt-da", lang);
5050 if (!res) {
5051 if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
5052 res = wait_file(chan, ints, "digits/morning", lang);
5053 else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
5054 res = wait_file(chan, ints, "digits/afternoon", lang);
5055 else res = wait_file(chan, ints, "digits/night", lang);
5058 } else {
5059 if (tm.tm_hour > 12)
5060 res = wait_file(chan, ints, "digits/p-m", lang);
5061 else if (tm.tm_hour && tm.tm_hour < 12)
5062 res = wait_file(chan, ints, "digits/a-m", lang);
5064 break;
5065 case 'Q':
5066 /* Shorthand for "Today", "Yesterday", or ABdY */
5067 /* XXX As emphasized elsewhere, this should the native way in your
5068 * language to say the date, with changes in what you say, depending
5069 * upon how recent the date is. XXX */
5071 struct timeval now;
5072 struct tm tmnow;
5073 time_t beg_today, tt;
5075 gettimeofday(&now,NULL);
5076 tt = now.tv_sec;
5077 ast_localtime(&tt,&tmnow,timezone);
5078 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5079 /* In any case, it saves not having to do ast_mktime() */
5080 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5081 if (beg_today < time) {
5082 /* Today */
5083 res = wait_file(chan,ints, "digits/today",lang);
5084 } else if (beg_today - 86400 < time) {
5085 /* Yesterday */
5086 res = wait_file(chan,ints, "digits/yesterday",lang);
5087 } else {
5088 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5091 break;
5092 case 'q':
5093 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5094 /* XXX As emphasized elsewhere, this should the native way in your
5095 * language to say the date, with changes in what you say, depending
5096 * upon how recent the date is. XXX */
5098 struct timeval now;
5099 struct tm tmnow;
5100 time_t beg_today, tt;
5102 gettimeofday(&now,NULL);
5103 tt = now.tv_sec;
5104 ast_localtime(&tt,&tmnow,timezone);
5105 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5106 /* In any case, it saves not having to do ast_mktime() */
5107 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5108 if (beg_today < time) {
5109 /* Today */
5110 } else if ((beg_today - 86400) < time) {
5111 /* Yesterday */
5112 res = wait_file(chan,ints, "digits/yesterday",lang);
5113 } else if (beg_today - 86400 * 6 < time) {
5114 /* Within the last week */
5115 res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
5116 } else {
5117 res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
5120 break;
5121 case 'R':
5122 res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
5123 break;
5124 case 'S':
5125 /* Seconds */
5126 if (!strcasecmp(lang, "pt_BR")) {
5127 res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
5128 if (!res) {
5129 if (tm.tm_sec > 1) {
5130 res = wait_file(chan,ints,"digits/seconds",lang);
5131 } else {
5132 res = wait_file(chan,ints,"digits/second",lang);
5134 } else if (tm.tm_sec < 10) {
5135 res = wait_file(chan,ints, "digits/oh",lang);
5136 if (!res) {
5137 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5138 res = wait_file(chan,ints,nextmsg,lang);
5140 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5141 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5142 res = wait_file(chan,ints,nextmsg,lang);
5143 } else {
5144 int ten, one;
5145 ten = (tm.tm_sec / 10) * 10;
5146 one = (tm.tm_sec % 10);
5147 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
5148 res = wait_file(chan,ints,nextmsg,lang);
5149 if (!res) {
5150 /* Fifty, not fifty-zero */
5151 if (one != 0) {
5152 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
5153 res = wait_file(chan,ints,nextmsg,lang);
5158 break;
5159 case 'T':
5160 res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
5161 break;
5162 case ' ':
5163 case ' ':
5164 /* Just ignore spaces and tabs */
5165 break;
5166 default:
5167 /* Unknown character */
5168 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5170 /* Jump out on DTMF */
5171 if (res) {
5172 break;
5175 return res;
5178 /* Taiwanese / Chinese syntax */
5179 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)
5181 struct tm tm;
5182 int res=0, offset, sndoffset;
5183 char sndfile[256], nextmsg[256];
5185 if (format == NULL)
5186 format = "YBdAkM";
5188 ast_localtime(&time,&tm,timezone);
5190 for (offset=0 ; format[offset] != '\0' ; offset++) {
5191 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5192 switch (format[offset]) {
5193 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5194 case '\'':
5195 /* Literal name of a sound file */
5196 sndoffset=0;
5197 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
5198 sndfile[sndoffset] = format[offset];
5199 sndfile[sndoffset] = '\0';
5200 res = wait_file(chan,ints,sndfile,lang);
5201 break;
5202 case 'A':
5203 case 'a':
5204 /* Sunday - Saturday */
5205 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5206 res = wait_file(chan,ints,nextmsg,lang);
5207 break;
5208 case 'B':
5209 case 'b':
5210 case 'h':
5211 /* January - December */
5212 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5213 res = wait_file(chan,ints,nextmsg,lang);
5214 break;
5215 case 'm':
5216 /* First - Twelfth */
5217 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5218 res = wait_file(chan,ints,nextmsg,lang);
5219 break;
5220 case 'd':
5221 case 'e':
5222 /* First - Thirtyfirst */
5223 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
5224 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
5225 res = wait_file(chan,ints,nextmsg,lang);
5226 } else {
5227 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
5228 res = wait_file(chan,ints,nextmsg,lang);
5229 if(!res) {
5230 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
5231 res = wait_file(chan,ints,nextmsg,lang);
5234 if(!res) res = wait_file(chan,ints,"ri",lang);
5235 break;
5236 case 'Y':
5237 /* Year */
5238 if (tm.tm_year > 99) {
5239 res = wait_file(chan,ints, "digits/2",lang);
5240 if (!res) {
5241 res = wait_file(chan,ints, "digits/thousand",lang);
5243 if (tm.tm_year > 100) {
5244 if (!res) {
5245 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
5246 res = wait_file(chan,ints,nextmsg,lang);
5247 if (!res) {
5248 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
5249 res = wait_file(chan,ints,nextmsg,lang);
5253 if (!res) {
5254 res = wait_file(chan,ints, "digits/year",lang);
5256 } else {
5257 if (tm.tm_year < 1) {
5258 /* I'm not going to handle 1900 and prior */
5259 /* We'll just be silent on the year, instead of bombing out. */
5260 } else {
5261 res = wait_file(chan,ints, "digits/1",lang);
5262 if (!res) {
5263 res = wait_file(chan,ints, "digits/9",lang);
5265 if (!res) {
5266 if (tm.tm_year <= 9) {
5267 /* 1901 - 1909 */
5268 res = wait_file(chan,ints, "digits/0",lang);
5269 if (!res) {
5270 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
5271 res = wait_file(chan,ints,nextmsg,lang);
5273 } else {
5274 /* 1910 - 1999 */
5275 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
5276 res = wait_file(chan,ints,nextmsg,lang);
5277 if (!res) {
5278 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
5279 res = wait_file(chan,ints,nextmsg,lang);
5284 if (!res) {
5285 res = wait_file(chan,ints, "digits/year",lang);
5288 break;
5289 case 'I':
5290 case 'l':
5291 /* 12-Hour */
5292 if (tm.tm_hour == 0)
5293 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
5294 else if (tm.tm_hour > 12)
5295 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5296 else
5297 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5298 res = wait_file(chan,ints,nextmsg,lang);
5299 if (!res) {
5300 res = wait_file(chan,ints, "digits/oclock",lang);
5302 break;
5303 case 'H':
5304 if (tm.tm_hour < 10) {
5305 res = wait_file(chan, ints, "digits/0", lang);
5307 case 'k':
5308 /* 24-Hour */
5309 if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
5310 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
5311 res = wait_file(chan,ints,nextmsg,lang);
5312 } else {
5313 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
5314 res = wait_file(chan,ints,nextmsg,lang);
5315 if (!res) {
5316 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
5317 res = wait_file(chan,ints,nextmsg,lang);
5320 if (!res) {
5321 res = wait_file(chan,ints, "digits/oclock",lang);
5323 break;
5324 case 'M':
5325 /* Minute */
5326 if (!(tm.tm_min % 10) || tm.tm_min < 10) {
5327 if (tm.tm_min < 10) {
5328 res = wait_file(chan, ints, "digits/0", lang);
5330 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
5331 res = wait_file(chan,ints,nextmsg,lang);
5332 } else {
5333 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
5334 res = wait_file(chan,ints,nextmsg,lang);
5335 if (!res) {
5336 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
5337 res = wait_file(chan,ints,nextmsg,lang);
5340 if (!res) {
5341 res = wait_file(chan,ints, "digits/minute",lang);
5343 break;
5344 case 'P':
5345 case 'p':
5346 /* AM/PM */
5347 if (tm.tm_hour > 11)
5348 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
5349 else
5350 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
5351 res = wait_file(chan,ints,nextmsg,lang);
5352 break;
5353 case 'Q':
5354 /* Shorthand for "Today", "Yesterday", or ABdY */
5355 /* XXX As emphasized elsewhere, this should the native way in your
5356 * language to say the date, with changes in what you say, depending
5357 * upon how recent the date is. XXX */
5359 struct timeval now;
5360 struct tm tmnow;
5361 time_t beg_today, tt;
5363 gettimeofday(&now,NULL);
5364 tt = now.tv_sec;
5365 ast_localtime(&tt,&tmnow,timezone);
5366 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5367 /* In any case, it saves not having to do ast_mktime() */
5368 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5369 if (beg_today < time) {
5370 /* Today */
5371 res = wait_file(chan,ints, "digits/today",lang);
5372 } else if (beg_today - 86400 < time) {
5373 /* Yesterday */
5374 res = wait_file(chan,ints, "digits/yesterday",lang);
5375 } else {
5376 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5379 break;
5380 case 'q':
5381 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5382 /* XXX As emphasized elsewhere, this should the native way in your
5383 * language to say the date, with changes in what you say, depending
5384 * upon how recent the date is. XXX */
5386 struct timeval now;
5387 struct tm tmnow;
5388 time_t beg_today, tt;
5390 gettimeofday(&now,NULL);
5391 tt = now.tv_sec;
5392 ast_localtime(&tt,&tmnow,timezone);
5393 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5394 /* In any case, it saves not having to do ast_mktime() */
5395 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5396 if (beg_today < time) {
5397 /* Today */
5398 } else if ((beg_today - 86400) < time) {
5399 /* Yesterday */
5400 res = wait_file(chan,ints, "digits/yesterday",lang);
5401 } else if (beg_today - 86400 * 6 < time) {
5402 /* Within the last week */
5403 res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
5404 } else {
5405 res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
5408 break;
5409 case 'R':
5410 res = ast_say_date_with_format_tw(chan, time, ints, lang, "kM", timezone);
5411 break;
5412 case 'S':
5413 /* Seconds */
5414 if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
5415 if (tm.tm_sec < 10) {
5416 res = wait_file(chan, ints, "digits/0", lang);
5418 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
5419 res = wait_file(chan,ints,nextmsg,lang);
5420 } else {
5421 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
5422 res = wait_file(chan,ints,nextmsg,lang);
5423 if (!res) {
5424 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
5425 res = wait_file(chan,ints,nextmsg,lang);
5428 if (!res) {
5429 res = wait_file(chan,ints, "digits/second",lang);
5431 break;
5432 case 'T':
5433 res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
5434 break;
5435 case ' ':
5436 case ' ':
5437 /* Just ignore spaces and tabs */
5438 break;
5439 default:
5440 /* Unknown character */
5441 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5443 /* Jump out on DTMF */
5444 if (res) {
5445 break;
5448 return res;
5451 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5453 if (!strcasecmp(lang, "en") ) { /* English syntax */
5454 return(ast_say_time_en(chan, t, ints, lang));
5455 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5456 return(ast_say_time_de(chan, t, ints, lang));
5457 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5458 return(ast_say_time_fr(chan, t, ints, lang));
5459 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5460 return(ast_say_time_nl(chan, t, ints, lang));
5461 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5462 return(ast_say_time_pt(chan, t, ints, lang));
5463 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5464 return(ast_say_time_pt_BR(chan, t, ints, lang));
5465 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5466 return(ast_say_time_tw(chan, t, ints, lang));
5467 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5468 return(ast_say_time_gr(chan, t, ints, lang));
5469 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5470 return(ast_say_time_ge(chan, t, ints, lang));
5473 /* Default to English */
5474 return(ast_say_time_en(chan, t, ints, lang));
5477 /* English syntax */
5478 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5480 struct tm tm;
5481 int res = 0;
5482 int hour, pm=0;
5484 ast_localtime(&t, &tm, NULL);
5485 hour = tm.tm_hour;
5486 if (!hour)
5487 hour = 12;
5488 else if (hour == 12)
5489 pm = 1;
5490 else if (hour > 12) {
5491 hour -= 12;
5492 pm = 1;
5494 if (!res)
5495 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5497 if (tm.tm_min > 9) {
5498 if (!res)
5499 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5500 } else if (tm.tm_min) {
5501 if (!res)
5502 res = ast_streamfile(chan, "digits/oh", lang);
5503 if (!res)
5504 res = ast_waitstream(chan, ints);
5505 if (!res)
5506 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5507 } else {
5508 if (!res)
5509 res = ast_streamfile(chan, "digits/oclock", lang);
5510 if (!res)
5511 res = ast_waitstream(chan, ints);
5513 if (pm) {
5514 if (!res)
5515 res = ast_streamfile(chan, "digits/p-m", lang);
5516 } else {
5517 if (!res)
5518 res = ast_streamfile(chan, "digits/a-m", lang);
5520 if (!res)
5521 res = ast_waitstream(chan, ints);
5522 return res;
5525 /* German syntax */
5526 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5528 struct tm tm;
5529 int res = 0;
5531 ast_localtime(&t, &tm, NULL);
5532 if (!res)
5533 res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5534 if (!res)
5535 res = ast_streamfile(chan, "digits/oclock", lang);
5536 if (!res)
5537 res = ast_waitstream(chan, ints);
5538 if (!res)
5539 if (tm.tm_min > 0)
5540 res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5541 return res;
5544 /* French syntax */
5545 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5547 struct tm tm;
5548 int res = 0;
5550 ast_localtime(&t, &tm, NULL);
5552 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5553 if (!res)
5554 res = ast_streamfile(chan, "digits/oclock", lang);
5555 if (tm.tm_min) {
5556 if (!res)
5557 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5559 return res;
5562 /* Dutch syntax */
5563 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5565 struct tm tm;
5566 int res = 0;
5568 ast_localtime(&t, &tm, NULL);
5569 if (!res)
5570 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5571 if (!res)
5572 res = ast_streamfile(chan, "digits/nl-uur", lang);
5573 if (!res)
5574 res = ast_waitstream(chan, ints);
5575 if (!res)
5576 if (tm.tm_min > 0)
5577 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
5578 return res;
5581 /* Portuguese syntax */
5582 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5584 struct tm tm;
5585 int res = 0;
5586 int hour;
5588 ast_localtime(&t, &tm, NULL);
5589 hour = tm.tm_hour;
5590 if (!res)
5591 res = ast_say_number(chan, hour, ints, lang, "f");
5592 if (tm.tm_min) {
5593 if (!res)
5594 res = wait_file(chan, ints, "digits/pt-e", lang);
5595 if (!res)
5596 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5597 } else {
5598 if (!res)
5599 res = wait_file(chan, ints, "digits/pt-hora", lang);
5600 if (tm.tm_hour != 1)
5601 if (!res)
5602 res = wait_file(chan, ints, "digits/pt-sss", lang);
5604 if (!res)
5605 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5606 return res;
5609 /* Brazilian Portuguese syntax */
5610 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5612 struct tm tm;
5613 int res = 0;
5615 ast_localtime(&t, &tm, NULL);
5617 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5618 if (!res) {
5619 if (tm.tm_hour > 1)
5620 res = wait_file(chan, ints, "digits/hours", lang);
5621 else
5622 res = wait_file(chan, ints, "digits/hour", lang);
5624 if ((!res) && (tm.tm_min)) {
5625 res = wait_file(chan, ints, "digits/pt-e", lang);
5626 if (!res)
5627 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5628 if (!res) {
5629 if (tm.tm_min > 1)
5630 res = wait_file(chan, ints, "digits/minutes", lang);
5631 else
5632 res = wait_file(chan, ints, "digits/minute", lang);
5635 return res;
5638 /* Taiwanese / Chinese syntax */
5639 int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5641 struct tm tm;
5642 int res = 0;
5643 int hour, pm=0;
5645 ast_localtime(&t, &tm, NULL);
5646 hour = tm.tm_hour;
5647 if (!hour)
5648 hour = 12;
5649 else if (hour == 12)
5650 pm = 1;
5651 else if (hour > 12) {
5652 hour -= 12;
5653 pm = 1;
5655 if (pm) {
5656 if (!res)
5657 res = ast_streamfile(chan, "digits/p-m", lang);
5658 } else {
5659 if (!res)
5660 res = ast_streamfile(chan, "digits/a-m", lang);
5662 if (!res)
5663 res = ast_waitstream(chan, ints);
5664 if (!res)
5665 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5666 if (!res)
5667 res = ast_streamfile(chan, "digits/oclock", lang);
5668 if (!res)
5669 res = ast_waitstream(chan, ints);
5670 if (!res)
5671 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5672 if (!res)
5673 res = ast_streamfile(chan, "digits/minute", lang);
5674 if (!res)
5675 res = ast_waitstream(chan, ints);
5676 return res;
5679 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5681 if (!strcasecmp(lang, "en") ) { /* English syntax */
5682 return(ast_say_datetime_en(chan, t, ints, lang));
5683 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
5684 return(ast_say_datetime_de(chan, t, ints, lang));
5685 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5686 return(ast_say_datetime_fr(chan, t, ints, lang));
5687 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
5688 return(ast_say_datetime_nl(chan, t, ints, lang));
5689 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
5690 return(ast_say_datetime_pt(chan, t, ints, lang));
5691 } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
5692 return(ast_say_datetime_pt_BR(chan, t, ints, lang));
5693 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
5694 return(ast_say_datetime_tw(chan, t, ints, lang));
5695 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
5696 return(ast_say_datetime_gr(chan, t, ints, lang));
5697 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5698 return(ast_say_datetime_ge(chan, t, ints, lang));
5701 /* Default to English */
5702 return(ast_say_datetime_en(chan, t, ints, lang));
5705 /* English syntax */
5706 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5708 struct tm tm;
5709 char fn[256];
5710 int res = 0;
5711 int hour, pm=0;
5713 ast_localtime(&t, &tm, NULL);
5714 if (!res) {
5715 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5716 res = ast_streamfile(chan, fn, lang);
5717 if (!res)
5718 res = ast_waitstream(chan, ints);
5720 if (!res) {
5721 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5722 res = ast_streamfile(chan, fn, lang);
5723 if (!res)
5724 res = ast_waitstream(chan, ints);
5726 if (!res)
5727 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5729 hour = tm.tm_hour;
5730 if (!hour)
5731 hour = 12;
5732 else if (hour == 12)
5733 pm = 1;
5734 else if (hour > 12) {
5735 hour -= 12;
5736 pm = 1;
5738 if (!res)
5739 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5741 if (tm.tm_min > 9) {
5742 if (!res)
5743 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5744 } else if (tm.tm_min) {
5745 if (!res)
5746 res = ast_streamfile(chan, "digits/oh", lang);
5747 if (!res)
5748 res = ast_waitstream(chan, ints);
5749 if (!res)
5750 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5751 } else {
5752 if (!res)
5753 res = ast_streamfile(chan, "digits/oclock", lang);
5754 if (!res)
5755 res = ast_waitstream(chan, ints);
5757 if (pm) {
5758 if (!res)
5759 res = ast_streamfile(chan, "digits/p-m", lang);
5760 } else {
5761 if (!res)
5762 res = ast_streamfile(chan, "digits/a-m", lang);
5764 if (!res)
5765 res = ast_waitstream(chan, ints);
5766 if (!res)
5767 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5768 return res;
5771 /* German syntax */
5772 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5774 struct tm tm;
5775 int res = 0;
5777 ast_localtime(&t, &tm, NULL);
5778 res = ast_say_date(chan, t, ints, lang);
5779 if (!res)
5780 ast_say_time(chan, t, ints, lang);
5781 return res;
5785 /* French syntax */
5786 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5788 struct tm tm;
5789 char fn[256];
5790 int res = 0;
5792 ast_localtime(&t, &tm, NULL);
5794 if (!res)
5795 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5797 if (!res) {
5798 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5799 res = ast_streamfile(chan, fn, lang);
5800 if (!res)
5801 res = ast_waitstream(chan, ints);
5803 if (!res) {
5804 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5805 res = ast_streamfile(chan, fn, lang);
5806 if (!res)
5807 res = ast_waitstream(chan, ints);
5810 if (!res)
5811 res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
5812 if (!res)
5813 res = ast_streamfile(chan, "digits/oclock", lang);
5814 if (tm.tm_min > 0) {
5815 if (!res)
5816 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5818 if (!res)
5819 res = ast_waitstream(chan, ints);
5820 if (!res)
5821 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5822 return res;
5825 /* Dutch syntax */
5826 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5828 struct tm tm;
5829 int res = 0;
5831 ast_localtime(&t, &tm, NULL);
5832 res = ast_say_date(chan, t, ints, lang);
5833 if (!res) {
5834 res = ast_streamfile(chan, "digits/nl-om", lang);
5835 if (!res)
5836 res = ast_waitstream(chan, ints);
5838 if (!res)
5839 ast_say_time(chan, t, ints, lang);
5840 return res;
5843 /* Portuguese syntax */
5844 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5846 struct tm tm;
5847 char fn[256];
5848 int res = 0;
5849 int hour, pm=0;
5851 ast_localtime(&t, &tm, NULL);
5852 if (!res) {
5853 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5854 res = ast_streamfile(chan, fn, lang);
5855 if (!res)
5856 res = ast_waitstream(chan, ints);
5858 if (!res) {
5859 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5860 res = ast_streamfile(chan, fn, lang);
5861 if (!res)
5862 res = ast_waitstream(chan, ints);
5864 if (!res)
5865 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5867 hour = tm.tm_hour;
5868 if (!hour)
5869 hour = 12;
5870 else if (hour == 12)
5871 pm = 1;
5872 else if (hour > 12) {
5873 hour -= 12;
5874 pm = 1;
5876 if (!res)
5877 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5879 if (tm.tm_min > 9) {
5880 if (!res)
5881 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5882 } else if (tm.tm_min) {
5883 if (!res)
5884 res = ast_streamfile(chan, "digits/oh", lang);
5885 if (!res)
5886 res = ast_waitstream(chan, ints);
5887 if (!res)
5888 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5889 } else {
5890 if (!res)
5891 res = ast_streamfile(chan, "digits/oclock", lang);
5892 if (!res)
5893 res = ast_waitstream(chan, ints);
5895 if (pm) {
5896 if (!res)
5897 res = ast_streamfile(chan, "digits/p-m", lang);
5898 } else {
5899 if (!res)
5900 res = ast_streamfile(chan, "digits/a-m", lang);
5902 if (!res)
5903 res = ast_waitstream(chan, ints);
5904 if (!res)
5905 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5906 return res;
5909 /* Brazilian Portuguese syntax */
5910 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5912 struct tm tm;
5913 int res = 0;
5915 ast_localtime(&t, &tm, NULL);
5916 res = ast_say_date(chan, t, ints, lang);
5917 if (!res)
5918 res = ast_say_time(chan, t, ints, lang);
5919 return res;
5922 /* Taiwanese / Chinese syntax */
5923 int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5925 struct tm tm;
5926 char fn[256];
5927 int res = 0;
5928 int hour, pm=0;
5930 ast_localtime(&t, &tm, NULL);
5931 if (!res)
5932 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5933 if (!res) {
5934 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
5935 res = ast_streamfile(chan, fn, lang);
5936 if (!res)
5937 res = ast_waitstream(chan, ints);
5939 if (!res)
5940 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5941 if (!res) {
5942 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
5943 res = ast_streamfile(chan, fn, lang);
5944 if (!res)
5945 res = ast_waitstream(chan, ints);
5948 hour = tm.tm_hour;
5949 if (!hour)
5950 hour = 12;
5951 else if (hour == 12)
5952 pm = 1;
5953 else if (hour > 12) {
5954 hour -= 12;
5955 pm = 1;
5957 if (pm) {
5958 if (!res)
5959 res = ast_streamfile(chan, "digits/p-m", lang);
5960 } else {
5961 if (!res)
5962 res = ast_streamfile(chan, "digits/a-m", lang);
5964 if (!res)
5965 res = ast_waitstream(chan, ints);
5966 if (!res)
5967 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
5968 if (!res)
5969 res = ast_streamfile(chan, "digits/oclock", lang);
5970 if (!res)
5971 res = ast_waitstream(chan, ints);
5972 if (!res)
5973 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5974 if (!res)
5975 res = ast_streamfile(chan, "digits/minute", lang);
5976 if (!res)
5977 res = ast_waitstream(chan, ints);
5978 return res;
5981 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
5983 if (!strcasecmp(lang, "en") ) { /* English syntax */
5984 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5985 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
5986 return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
5987 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
5988 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
5989 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
5990 return(ast_say_datetime_from_now_ge(chan, t, ints, lang));
5993 /* Default to English */
5994 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
5997 /* English syntax */
5998 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6000 int res=0;
6001 time_t nowt;
6002 int daydiff;
6003 struct tm tm;
6004 struct tm now;
6005 char fn[256];
6007 time(&nowt);
6009 ast_localtime(&t, &tm, NULL);
6010 ast_localtime(&nowt,&now, NULL);
6011 daydiff = now.tm_yday - tm.tm_yday;
6012 if ((daydiff < 0) || (daydiff > 6)) {
6013 /* Day of month and month */
6014 if (!res) {
6015 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6016 res = ast_streamfile(chan, fn, lang);
6017 if (!res)
6018 res = ast_waitstream(chan, ints);
6020 if (!res)
6021 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6023 } else if (daydiff) {
6024 /* Just what day of the week */
6025 if (!res) {
6026 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6027 res = ast_streamfile(chan, fn, lang);
6028 if (!res)
6029 res = ast_waitstream(chan, ints);
6031 } /* Otherwise, it was today */
6032 if (!res)
6033 res = ast_say_time(chan, t, ints, lang);
6034 return res;
6037 /* French syntax */
6038 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6040 int res=0;
6041 time_t nowt;
6042 int daydiff;
6043 struct tm tm;
6044 struct tm now;
6045 char fn[256];
6047 time(&nowt);
6049 ast_localtime(&t, &tm, NULL);
6050 ast_localtime(&nowt, &now, NULL);
6051 daydiff = now.tm_yday - tm.tm_yday;
6052 if ((daydiff < 0) || (daydiff > 6)) {
6053 /* Day of month and month */
6054 if (!res) {
6055 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6056 res = ast_streamfile(chan, fn, lang);
6057 if (!res)
6058 res = ast_waitstream(chan, ints);
6060 if (!res)
6061 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6063 } else if (daydiff) {
6064 /* Just what day of the week */
6065 if (!res) {
6066 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6067 res = ast_streamfile(chan, fn, lang);
6068 if (!res)
6069 res = ast_waitstream(chan, ints);
6071 } /* Otherwise, it was today */
6072 if (!res)
6073 res = ast_say_time(chan, t, ints, lang);
6074 return res;
6077 /* Portuguese syntax */
6078 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6080 int res=0;
6081 time_t nowt;
6082 int daydiff;
6083 struct tm tm;
6084 struct tm now;
6085 char fn[256];
6087 time(&nowt);
6089 ast_localtime(&t, &tm, NULL);
6090 ast_localtime(&nowt, &now, NULL);
6091 daydiff = now.tm_yday - tm.tm_yday;
6092 if ((daydiff < 0) || (daydiff > 6)) {
6093 /* Day of month and month */
6094 if (!res)
6095 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6096 if (!res)
6097 res = wait_file(chan, ints, "digits/pt-de", lang);
6098 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6099 if (!res)
6100 res = wait_file(chan, ints, fn, lang);
6102 } else if (daydiff) {
6103 /* Just what day of the week */
6104 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6105 if (!res)
6106 res = wait_file(chan, ints, fn, lang);
6107 } /* Otherwise, it was today */
6108 if (!strcasecmp(lang, "pt_BR")) {
6109 if (tm.tm_hour > 1) {
6110 snprintf(fn, sizeof(fn), "digits/pt-as");
6111 } else {
6112 snprintf(fn, sizeof(fn), "digits/pt-a");
6114 if (!res)
6115 res = wait_file(chan, ints, fn, lang);
6116 } else {
6117 snprintf(fn, sizeof(fn), "digits/pt-ah");
6118 if (!res)
6119 res = wait_file(chan, ints, fn, lang);
6120 if (tm.tm_hour != 1)
6121 if (!res)
6122 res = wait_file(chan, ints, "digits/pt-sss", lang);
6123 if (!res)
6124 res = ast_say_time(chan, t, ints, lang);
6126 return res;
6130 /*********************************** GREEK SUPPORT ***************************************/
6134 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6136 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
6137 int tmp;
6138 int left;
6139 int res;
6140 char fn[256] = "";
6142 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6143 if (num < 5) {
6144 snprintf(fn, sizeof(fn), "digits/female-%d", num);
6145 res = wait_file(chan, ints, fn, lang);
6146 } else if (num < 13) {
6147 res = ast_say_number(chan, num, ints, lang, (char *) NULL);
6148 } else if (num <100 ) {
6149 tmp = (num/10) * 10;
6150 left = num - tmp;
6151 snprintf(fn, sizeof(fn), "digits/%d", tmp);
6152 res = ast_streamfile(chan, fn, lang);
6153 if (!res)
6154 res = ast_waitstream(chan, ints);
6155 if (left)
6156 gr_say_number_female(left, chan, ints, lang);
6158 } else {
6159 return -1;
6161 return res;
6167 * A list of the files that you need to create
6168 -> digits/xilia = "xilia"
6169 -> digits/myrio = "ekatomyrio"
6170 -> digits/thousands = "xiliades"
6171 -> digits/millions = "ektatomyria"
6172 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6173 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6174 e,g 80 = "ogdonta"
6175 Here we must note that we use digits/tens/100 to utter "ekato"
6176 and digits/hundred-100 to utter "ekaton"
6177 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6178 "terakosia". Here again we use hundreds/1000 for "xilia"
6179 and digits/thousnds for "xiliades"
6182 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
6184 int res = 0;
6185 char fn[256] = "";
6186 int i=0;
6189 if (!num) {
6190 snprintf(fn, sizeof(fn), "digits/0");
6191 res = ast_streamfile(chan, fn, chan->language);
6192 if (!res)
6193 return ast_waitstream(chan, ints);
6196 while(!res && num ) {
6197 i++;
6198 if (num < 13) {
6199 snprintf(fn, sizeof(fn), "digits/%d", num);
6200 num = 0;
6201 } else if (num <= 100) {
6202 /* 13 < num <= 100 */
6203 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
6204 num -= ((num / 10) * 10);
6205 } else if (num < 200) {
6206 /* 100 < num < 200 */
6207 snprintf(fn, sizeof(fn), "digits/hundred-100");
6208 num -= ((num / 100) * 100);
6209 }else if (num < 1000) {
6210 /* 200 < num < 1000 */
6211 snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
6212 num -= ((num / 100) * 100);
6213 }else if (num < 2000){
6214 snprintf(fn, sizeof(fn), "digits/xilia");
6215 num -= ((num / 1000) * 1000);
6217 else {
6218 /* num > 1000 */
6219 if (num < 1000000) {
6220 res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
6221 if (res)
6222 return res;
6223 num = num % 1000;
6224 snprintf(fn, sizeof(fn), "digits/thousands");
6225 } else {
6226 if (num < 1000000000) { /* 1,000,000,000 */
6227 res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
6228 if (res)
6229 return res;
6230 num = num % 1000000;
6231 snprintf(fn, sizeof(fn), "digits/millions");
6232 } else {
6233 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
6234 res = -1;
6238 if (!res) {
6239 if(!ast_streamfile(chan, fn, language)) {
6240 if ((audiofd > -1) && (ctrlfd > -1))
6241 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6242 else
6243 res = ast_waitstream(chan, ints);
6245 ast_stopstream(chan);
6248 return res;
6253 * The format is weekday - day - month -year
6255 * A list of the files that you need to create
6256 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6257 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6258 Attention the months are in
6259 "gekinh klhsh"
6263 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6265 struct tm tm;
6267 char fn[256];
6268 int res = 0;
6271 ast_localtime(&t,&tm,NULL);
6272 /* W E E K - D A Y */
6273 if (!res) {
6274 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6275 res = ast_streamfile(chan, fn, lang);
6276 if (!res)
6277 res = ast_waitstream(chan, ints);
6279 /* D A Y */
6280 if (!res) {
6281 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6283 /* M O N T H */
6284 if (!res) {
6285 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6286 res = ast_streamfile(chan, fn, lang);
6287 if (!res)
6288 res = ast_waitstream(chan, ints);
6290 /* Y E A R */
6291 if (!res)
6292 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6293 return res;
6298 /* A list of the files that you need to create
6299 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6300 * digits/kai : "KAI"
6301 * didgits : "h wra"
6302 * digits/p-m : "meta meshmbrias"
6303 * digits/a-m : "pro meshmbrias"
6306 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6309 struct tm tm;
6310 int res = 0;
6311 int hour, pm=0;
6313 ast_localtime(&t, &tm, NULL);
6314 hour = tm.tm_hour;
6316 if (!hour)
6317 hour = 12;
6318 else if (hour == 12)
6319 pm = 1;
6320 else if (hour > 12) {
6321 hour -= 12;
6322 pm = 1;
6325 res = gr_say_number_female(hour, chan, ints, lang);
6326 if (tm.tm_min) {
6327 if (!res)
6328 res = ast_streamfile(chan, "digits/kai", lang);
6329 if (!res)
6330 res = ast_waitstream(chan, ints);
6331 if (!res)
6332 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6333 } else {
6334 if (!res)
6335 res = ast_streamfile(chan, "digits/hwra", lang);
6336 if (!res)
6337 res = ast_waitstream(chan, ints);
6339 if (pm) {
6340 if (!res)
6341 res = ast_streamfile(chan, "digits/p-m", lang);
6342 } else {
6343 if (!res)
6344 res = ast_streamfile(chan, "digits/a-m", lang);
6346 if (!res)
6347 res = ast_waitstream(chan, ints);
6348 return res;
6353 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6355 struct tm tm;
6356 char fn[256];
6357 int res = 0;
6359 ast_localtime(&t, &tm, NULL);
6362 /* W E E K - D A Y */
6363 if (!res) {
6364 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6365 res = ast_streamfile(chan, fn, lang);
6366 if (!res)
6367 res = ast_waitstream(chan, ints);
6369 /* D A Y */
6370 if (!res) {
6371 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6373 /* M O N T H */
6374 if (!res) {
6375 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6376 res = ast_streamfile(chan, fn, lang);
6377 if (!res)
6378 res = ast_waitstream(chan, ints);
6381 res = ast_say_time_gr(chan, t, ints, lang);
6382 return res;
6385 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)
6388 struct tm tm;
6389 int res=0, offset, sndoffset;
6390 char sndfile[256], nextmsg[256];
6392 if (!format)
6393 format = "AdBY 'digits/at' IMp";
6395 ast_localtime(&time,&tm,timezone);
6397 for (offset=0 ; format[offset] != '\0' ; offset++) {
6398 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6399 switch (format[offset]) {
6400 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6401 case '\'':
6402 /* Literal name of a sound file */
6403 sndoffset=0;
6404 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
6405 sndfile[sndoffset] = format[offset];
6406 sndfile[sndoffset] = '\0';
6407 res = wait_file(chan,ints,sndfile,lang);
6408 break;
6409 case 'A':
6410 case 'a':
6411 /* Sunday - Saturday */
6412 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6413 res = wait_file(chan,ints,nextmsg,lang);
6414 break;
6415 case 'B':
6416 case 'b':
6417 case 'h':
6418 /* January - December */
6419 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6420 res = wait_file(chan,ints,nextmsg,lang);
6421 break;
6422 case 'd':
6423 case 'e':
6424 /* first - thirtyfirst */
6425 gr_say_number_female(tm.tm_mday, chan, ints, lang);
6426 break;
6427 case 'Y':
6428 /* Year */
6430 ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
6431 break;
6432 case 'I':
6433 case 'l':
6434 /* 12-Hour */
6435 if (tm.tm_hour == 0)
6436 gr_say_number_female(12, chan, ints, lang);
6437 else if (tm.tm_hour > 12)
6438 gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
6439 else
6440 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6441 break;
6442 case 'H':
6443 case 'k':
6444 /* 24-Hour */
6445 gr_say_number_female(tm.tm_hour, chan, ints, lang);
6446 break;
6447 case 'M':
6448 /* Minute */
6449 if (tm.tm_min) {
6450 if (!res)
6451 res = ast_streamfile(chan, "digits/kai", lang);
6452 if (!res)
6453 res = ast_waitstream(chan, ints);
6454 if (!res)
6455 res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
6456 } else {
6457 if (!res)
6458 res = ast_streamfile(chan, "digits/oclock", lang);
6459 if (!res)
6460 res = ast_waitstream(chan, ints);
6462 break;
6463 case 'P':
6464 case 'p':
6465 /* AM/PM */
6466 if (tm.tm_hour > 11)
6467 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
6468 else
6469 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
6470 res = wait_file(chan,ints,nextmsg,lang);
6471 break;
6472 case 'Q':
6473 /* Shorthand for "Today", "Yesterday", or ABdY */
6474 /* XXX As emphasized elsewhere, this should the native way in your
6475 * language to say the date, with changes in what you say, depending
6476 * upon how recent the date is. XXX */
6478 struct timeval now;
6479 struct tm tmnow;
6480 time_t beg_today, tt;
6482 gettimeofday(&now,NULL);
6483 tt = now.tv_sec;
6484 ast_localtime(&tt,&tmnow,timezone);
6485 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6486 /* In any case, it saves not having to do ast_mktime() */
6487 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6488 if (beg_today < time) {
6489 /* Today */
6490 res = wait_file(chan,ints, "digits/today",lang);
6491 } else if (beg_today - 86400 < time) {
6492 /* Yesterday */
6493 res = wait_file(chan,ints, "digits/yesterday",lang);
6494 } else {
6495 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6498 break;
6499 case 'q':
6500 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6501 /* XXX As emphasized elsewhere, this should the native way in your
6502 * language to say the date, with changes in what you say, depending
6503 * upon how recent the date is. XXX */
6505 struct timeval now;
6506 struct tm tmnow;
6507 time_t beg_today, tt;
6509 gettimeofday(&now,NULL);
6510 tt = now.tv_sec;
6511 ast_localtime(&tt,&tmnow,timezone);
6512 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6513 /* In any case, it saves not having to do ast_mktime() */
6514 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6515 if (beg_today < time) {
6516 /* Today */
6517 } else if ((beg_today - 86400) < time) {
6518 /* Yesterday */
6519 res = wait_file(chan,ints, "digits/yesterday",lang);
6520 } else if (beg_today - 86400 * 6 < time) {
6521 /* Within the last week */
6522 res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
6523 } else {
6524 res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
6527 break;
6528 case 'R':
6529 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
6530 break;
6531 case 'S':
6532 /* Seconds */
6533 snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
6534 res = wait_file(chan,ints,nextmsg,lang);
6535 if (!res)
6536 res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
6537 if (!res)
6538 snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
6539 res = wait_file(chan,ints,nextmsg,lang);
6540 break;
6541 case 'T':
6542 res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
6543 break;
6544 case ' ':
6545 case ' ':
6546 /* Just ignore spaces and tabs */
6547 break;
6548 default:
6549 /* Unknown character */
6550 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6552 /* Jump out on DTMF */
6553 if (res) {
6554 break;
6557 return res;
6563 /*********************************** Georgian Support ***************************************/
6567 Convert a number into a semi-localized string. Only for Georgian.
6568 res must be of at least 256 bytes, preallocated.
6569 The output corresponds to Georgian spoken numbers, so
6570 it may be either converted to real words by applying a direct conversion
6571 table, or played just by substituting the entities with played files.
6573 Output may consist of the following tokens (separated by spaces):
6574 0, minus.
6575 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6576 10-19.
6577 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6578 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6579 1000, 1000_. (atasi, atas).
6580 1000000, 1000000_. (milioni, milion).
6581 1000000000, 1000000000_. (miliardi, miliard).
6583 To be able to play the sounds, each of the above tokens needs
6584 a corresponding sound file. (e.g. 200_.gsm).
6586 static char* ast_translate_number_ge(int num, char* res, int res_len)
6588 char buf[256];
6589 int digit = 0;
6590 int remainder = 0;
6593 if (num < 0) {
6594 strncat(res, "minus ", res_len - strlen(res) - 1);
6595 if ( num > INT_MIN ) {
6596 num = -num;
6597 } else {
6598 num = 0;
6603 /* directly read the numbers */
6604 if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
6605 snprintf(buf, sizeof(buf), "%d", num);
6606 strncat(res, buf, res_len - strlen(res) - 1);
6607 return res;
6611 if (num < 40) { /* ocda... */
6612 strncat(res, "20_ ", res_len - strlen(res) - 1);
6613 return ast_translate_number_ge(num - 20, res, res_len);
6616 if (num < 60) { /* ormocda... */
6617 strncat(res, "40_ ", res_len - strlen(res) - 1);
6618 return ast_translate_number_ge(num - 40, res, res_len);
6621 if (num < 80) { /* samocda... */
6622 strncat(res, "60_ ", res_len - strlen(res) - 1);
6623 return ast_translate_number_ge(num - 60, res, res_len);
6626 if (num < 100) { /* otxmocda... */
6627 strncat(res, "80_ ", res_len - strlen(res) - 1);
6628 return ast_translate_number_ge(num - 80, res, res_len);
6632 if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6633 remainder = num % 100;
6634 digit = (num - remainder) / 100;
6636 if (remainder == 0) {
6637 snprintf(buf, sizeof(buf), "%d", num);
6638 strncat(res, buf, res_len - strlen(res) - 1);
6639 return res;
6640 } else {
6641 snprintf(buf, sizeof(buf), "%d_ ", digit*100);
6642 strncat(res, buf, res_len - strlen(res) - 1);
6643 return ast_translate_number_ge(remainder, res, res_len);
6648 if (num == 1000) {
6649 strncat(res, "1000", res_len - strlen(res) - 1);
6650 return res;
6654 if (num < 1000000) {
6655 remainder = num % 1000;
6656 digit = (num - remainder) / 1000;
6658 if (remainder == 0) {
6659 ast_translate_number_ge(digit, res, res_len);
6660 strncat(res, " 1000", res_len - strlen(res) - 1);
6661 return res;
6664 if (digit == 1) {
6665 strncat(res, "1000_ ", res_len - strlen(res) - 1);
6666 return ast_translate_number_ge(remainder, res, res_len);
6669 ast_translate_number_ge(digit, res, res_len);
6670 strncat(res, " 1000_ ", res_len - strlen(res) - 1);
6671 return ast_translate_number_ge(remainder, res, res_len);
6676 if (num == 1000000) {
6677 strncat(res, "1 1000000", res_len - strlen(res) - 1);
6678 return res;
6682 if (num < 1000000000) {
6683 remainder = num % 1000000;
6684 digit = (num - remainder) / 1000000;
6686 if (remainder == 0) {
6687 ast_translate_number_ge(digit, res, res_len);
6688 strncat(res, " 1000000", res_len - strlen(res) - 1);
6689 return res;
6692 ast_translate_number_ge(digit, res, res_len);
6693 strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
6694 return ast_translate_number_ge(remainder, res, res_len);
6699 if (num == 1000000000) {
6700 strncat(res, "1 1000000000", res_len - strlen(res) - 1);
6701 return res;
6705 if (num > 1000000000) {
6706 remainder = num % 1000000000;
6707 digit = (num - remainder) / 1000000000;
6709 if (remainder == 0) {
6710 ast_translate_number_ge(digit, res, res_len);
6711 strncat(res, " 1000000000", res_len - strlen(res) - 1);
6712 return res;
6715 ast_translate_number_ge(digit, res, res_len);
6716 strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
6717 return ast_translate_number_ge(remainder, res, res_len);
6721 return res;
6727 /*! \brief ast_say_number_full_ge: Georgian syntax */
6728 static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
6730 int res = 0;
6731 char fn[512] = "";
6732 if (!num)
6733 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
6736 ast_translate_number_ge(num, fn, 512);
6739 char* s = 0;
6740 const char* remainder = fn;
6742 while (res == 0 && (s = strstr(remainder, " "))) {
6743 size_t len = s - remainder;
6744 char* new_string = malloc(len + 1 + strlen("digits/"));
6746 sprintf(new_string, "digits/");
6747 strncat(new_string, remainder, len); /* we can't sprintf() it, it's not null-terminated. */
6748 /* new_string[len + strlen("digits/")] = '\0'; */
6750 if (!ast_streamfile(chan, new_string, language)) {
6751 if ((audiofd > -1) && (ctrlfd > -1))
6752 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6753 else
6754 res = ast_waitstream(chan, ints);
6756 ast_stopstream(chan);
6758 free(new_string);
6760 remainder = s + 1; /* position just after the found space char. */
6761 while(*remainder == ' ') /* skip multiple spaces */
6762 remainder++;
6766 /* the last chunk. */
6767 if (res == 0 && *remainder) {
6769 char* new_string = malloc(strlen(remainder) + 1 + strlen("digits/"));
6770 sprintf(new_string, "digits/%s", remainder);
6772 if (!ast_streamfile(chan, new_string, language)) {
6773 if ((audiofd > -1) && (ctrlfd > -1))
6774 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
6775 else
6776 res = ast_waitstream(chan, ints);
6778 ast_stopstream(chan);
6780 free(new_string);
6785 return res;
6792 Georgian support for date/time requires the following files (*.gsm):
6794 mon-1, mon-2, ... (ianvari, tebervali, ...)
6795 day-1, day-2, ... (orshabati, samshabati, ...)
6796 saati_da
6797 tsuti
6798 tslis
6803 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
6804 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6806 struct tm tm;
6807 char fn[256];
6808 int res = 0;
6809 ast_localtime(&t,&tm,NULL);
6811 if (!res)
6812 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6814 if (!res) {
6815 snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
6816 res = ast_streamfile(chan, fn, lang);
6817 if (!res)
6818 res = ast_waitstream(chan, ints);
6821 if (!res) {
6822 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
6823 /* if (!res)
6824 res = ast_waitstream(chan, ints);
6828 if (!res) {
6829 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6830 res = ast_streamfile(chan, fn, lang);
6831 if (!res)
6832 res = ast_waitstream(chan, ints);
6834 return res;
6842 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
6843 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6845 struct tm tm;
6846 int res = 0;
6848 ast_localtime(&t, &tm, NULL);
6850 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
6851 if (!res) {
6852 res = ast_streamfile(chan, "digits/saati_da", lang);
6853 if (!res)
6854 res = ast_waitstream(chan, ints);
6857 if (tm.tm_min) {
6858 if (!res) {
6859 res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
6861 if (!res) {
6862 res = ast_streamfile(chan, "digits/tsuti", lang);
6863 if (!res)
6864 res = ast_waitstream(chan, ints);
6868 return res;
6873 /* Georgian syntax. Say date, then say time. */
6874 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6876 struct tm tm;
6877 int res = 0;
6879 ast_localtime(&t, &tm, NULL);
6880 res = ast_say_date(chan, t, ints, lang);
6881 if (!res)
6882 ast_say_time(chan, t, ints, lang);
6883 return res;
6890 /* Georgian syntax */
6891 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
6893 int res=0;
6894 time_t nowt;
6895 int daydiff;
6896 struct tm tm;
6897 struct tm now;
6898 char fn[256];
6900 time(&nowt);
6902 ast_localtime(&t, &tm, NULL);
6903 ast_localtime(&nowt, &now, NULL);
6904 daydiff = now.tm_yday - tm.tm_yday;
6905 if ((daydiff < 0) || (daydiff > 6)) {
6906 /* Day of month and month */
6907 if (!res)
6908 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6909 if (!res) {
6910 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
6911 res = ast_streamfile(chan, fn, lang);
6912 if (!res)
6913 res = ast_waitstream(chan, ints);
6916 } else if (daydiff) {
6917 /* Just what day of the week */
6918 if (!res) {
6919 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
6920 res = ast_streamfile(chan, fn, lang);
6921 if (!res)
6922 res = ast_waitstream(chan, ints);
6924 } /* Otherwise, it was today */
6925 if (!res)
6926 res = ast_say_time(chan, t, ints, lang);
6928 return res;
6934 * remap the 'say' functions to use those in this file
6936 static void __attribute__((constructor)) __say_init(void)
6938 ast_say_number_full = say_number_full;
6939 ast_say_enumeration_full = say_enumeration_full;
6940 ast_say_digit_str_full = say_digit_str_full;
6941 ast_say_character_str_full = say_character_str_full;
6942 ast_say_phonetic_str_full = say_phonetic_str_full;
6943 ast_say_datetime = say_datetime;
6944 ast_say_time = say_time;
6945 ast_say_date = say_date;
6946 ast_say_datetime_from_now = say_datetime_from_now;
6947 ast_say_date_with_format = say_date_with_format;